1

I am trying to extract all layer/group ID names and metadata from the top layer as well as all layers underneath it.

Desired result: Get a list of IDs and Group IDs from an SVG. In example SVG would see, if hovering over the center of the image, something like this:

Poly 1 Star Group Begin: Bright Shapes Group Begin: Gradient Shapes Gradient Poly Small Gradient Poly Large Gradient Star End Group: Gradient Shapes Pink Ellipse Green Square End Group: Bright Shapes Ellipse 2


I've also looked at CSS pointer-events.. but that seems limited to extracting from 1 layer.

Modified from: Determine which element the mouse pointer is on top of in Javascript

http://jsfiddle.net/coq4eg0d/1/

setInterval(function(){
    
    var element = $(':hover');
    if(element.length)
    {
        var domElement = element[element.length - 1];
        var tagName = domElement.tagName;
        var id = domElement.id ? ' id="' + domElement.id + '"' : "";
        
        document.getElementById('test').innerHTML =
        "hover: <" + tagName.toLowerCase() + id + ">";
    }
}, 100);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="test"></div>
<svg version="1.1" id="Shapes" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 368 373" style="enable-background:new 0 0 368 373;" xml:space="preserve">
<style type="text/css">
    .st0{stroke:#FFFFFF;stroke-miterlimit:10;}
    .st1{fill:#4BB749;stroke:#1F451F;stroke-miterlimit:10;}
    .st2{fill:#CB3694;stroke:#FFFFFF;stroke-miterlimit:10;}
    .st3{fill:url(#Gradient_Star_1_);stroke:#FFFFFF;stroke-miterlimit:10;}
    .st4{fill:url(#Gradient_Poly_Large_1_);stroke:#FFFFFF;stroke-miterlimit:10;}
    .st5{fill:url(#Gradient_Poly_Small_1_);stroke:#FFFFFF;stroke-miterlimit:10;}
</style>
<ellipse id="Ellipse_2" class="st0" cx="261" cy="212" rx="94" ry="79"/>
<g id="Bright_Shapes">
    <rect id="Green_Square" x="98" y="161.22" class="st1" width="184" height="174.5"/>
    <ellipse id="Pink_Ellipse" class="st2" cx="115.59" cy="268.59" rx="94" ry="79"/>
    <g id="Gradient_Shapes">
        <radialGradient id="Gradient_Star_1_" cx="115.5932" cy="189.5899" r="147.9309" gradientUnits="userSpaceOnUse">
            <stop  offset="0" style="stop-color:#FFFFFF"/>
            <stop  offset="1" style="stop-color:#1C75BC"/>
        </radialGradient>
        <polygon id="Gradient_Star" class="st3" points="109.37,335.15 68.3,238.41 -34.67,217.41 44.63,148.46 32.79,44.03 122.87,98.15 
            218.53,54.61 194.9,157.01 265.86,234.53 161.17,243.7        "/>
        <radialGradient id="Gradient_Poly_Large_1_" cx="167" cy="171" r="88.1134" gradientUnits="userSpaceOnUse">
            <stop  offset="0" style="stop-color:#FFFFFF"/>
            <stop  offset="1" style="stop-color:#1C75BC"/>
        </radialGradient>
        <polygon id="Gradient_Poly_Large" class="st4" points="234,233 146.81,260.02 79.81,198.02 100,109 187.19,81.98 254.19,143.98         
            "/>
        <radialGradient id="Gradient_Poly_Small_1_" cx="209.5932" cy="248.4668" r="30.3834" gradientUnits="userSpaceOnUse">
            <stop  offset="0" style="stop-color:#FFFFFF"/>
            <stop  offset="1" style="stop-color:#1C75BC"/>
        </radialGradient>
        <polygon id="Gradient_Poly_Small" class="st5" points="236.59,265.88 208.02,280.55 181.02,263.14 182.59,231.06 211.17,216.38 
            238.17,233.79       "/>
    </g>
</g>
<g id="Pointed_Dark_Shapes">
    <polygon id="Star" class="st0" points="249,229 142.84,198.45 57.49,268.59 53.74,158.19 -39.34,98.69 64.5,61 92.32,-45.91 
        160.25,41.21 270.53,34.63 208.67,126.16     "/>
    <polygon id="Poly1" class="st0" points="220,251 124.55,266.33 63.55,191.33 98,101 193.45,85.67 254.45,160.67    "/>
</g>
</svg>
enxaneta
  • 31,608
  • 5
  • 29
  • 42
B D
  • 303
  • 3
  • 12

1 Answers1

0

One way to find all elements at a location is to call elementFromPoint() to find the topmost element, and then use the fact that..

Elements with pointer-events set to none will be ignored, and the element below it will be returned.

So, once you've found an element, set its pointer-events: none and then call elementFromPoint() again to find the next element. Repeat until you reach the main SVG element:

function hitTest(e) {
    const x = e.clientX,
          y = e.clientY,
          elms = [];

    let elm;
    while(true) {
        elm = document.elementFromPoint(x, y);
        if(!svg.contains(elm)) {
            break;
        }

        elms.push(elm);
        //Hide the element from the next round of `elementFromPoint()`:
        elm.style.pointerEvents = 'none';
    }
    
    output.textContent = elms.map(printElement).join(' ');

    //Cleanup:
    elms.forEach(elm => elm.style.pointerEvents = '');
}

http://jsfiddle.net/r9k1bgnu/

Sphinxxx
  • 12,484
  • 4
  • 54
  • 84
  • Thanks! This is amazing. Is there a way to also show the group IDs? – B D Jun 30 '20 at 23:41
  • For each element you find, check `elm.parentElement` to find its parent group. – Sphinxxx Jul 01 '20 at 04:03
  • Thanks! And last question on this.. I appreciate your help. Is this impossible with an externally loaded .svg file? Does it have to be inline? For example, load https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg – B D Jul 01 '20 at 16:11
  • For example, I added and changed in JS #Shapes to #tiger Also tried #svg2 which is the parent tag in the actual SVG... – B D Jul 01 '20 at 16:23
  • See jbeard4's answer here on how to access an `` embedded SVG: https://stackoverflow.com/a/3379830/1869660 Note what it says about the same-origin policy though, so loading the SVG from Wikipedia won't work. – Sphinxxx Jul 01 '20 at 20:05