With JavaScript, can I change the Z index/layer of an SVG <g> element?
Asked Answered
Z

9

92

Let's say I have a couple composite shapes (<g>). I want to be able to click and drag them, but I want the one I happen to be dragging at the moment to be on TOP of the other one in the Z order, so that if I drag it over the OTHER one, the other one should be eclipsed.

Zuckerman answered 27/1, 2009 at 2:11 Comment(2)
see here.Torrell
possible duplicate of SVG re-ordering z-index (Raphael optional)Torrell
S
119

Z index in SVG is defined by the order the elements appear in the document (subsequent elements are painted on top of previous elements).

You will have to change the element order if you want to bring a specific shape to the top.

Stupor answered 27/1, 2009 at 2:27 Comment(4)
@theonlygusti Elements at the end of the document are at the top. If you're using jQuery, you ought to be able to use something like $('#thing-i-want-on-top').appendTo('#my-svg');Teleutospore
So, basically, there's no way to float a shape to the top (z-axis) on hover…?Germanous
jQuery doesn't support changing order of SVG paths.Groceries
@chharvey, you can float things to the top by re-ordering them - re-appending. Here is an elegant example: codepen.io/osublake/pen/YXoEQePortwine
S
41

An alternative to moving elements in the tree is to use <use> elements where you change the xlink:href attribute so that it gives you the z ordering you want.

Here's an old thread on svg-developers mailinglist discussing this topic in context of wanting to animate some shapes.

Update:

<svg xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink"
     style="width:100%; height: 100%">
    <circle id="c1" cx="50" cy="50" r="40" fill="lime" />
    <rect id="r1" x="4" y="20" width="200" height="50" fill="cyan" />
    <circle id="c2" cx="70" cy="70" r="50" fill="fuchsia" />
    <use id="use" xlink:href="#c1" />
</svg>

In this example the <use> element is the last one, which makes it the frontmost element. We can choose any of the other elements to act as frontmost simply by changing the xlink:href attribute. In the above example we picked the circle with id="c1", which makes it appear as the topmost element.

See fiddle.

Starvation answered 9/6, 2011 at 8:3 Comment(5)
This answer would be much more useful with an example, or with the info from that mailing list (which is gated).Demanding
Works, but make you add use appropriately. If you use translations, <use> has no idea about themBoniface
@JochenBedersdorfer not sure what you mean, how are translations related to z ordering?Korn
Wasn't very clear about that. If you use nested <g>-tags with transforms, you need to convert the coordinates using getTransformToElement(..); (see SVG docs)Boniface
xlink:href is deprecated, now it's just hrefWondering
S
26

This is old question, but...

On FireFox (7+) and Chrome (14+) you can pull svg_element to the top. This does not give you freedom of full z axis control, but it's better than nothing ;)

Just append that element again.

var svg = doc.createElemNS('svg');
var circle = doc.createElemNS('circle');
var line = doc.createElemNS('line');

svg.appendChild(circle); // appends it
svg.appendChild(line);   // appends it over circle
svg.appendChild(circle); // redraws it over line now

I thought it was going to throw en error or something.

appendChild == replace itself == redraw

Sewan answered 7/11, 2011 at 23:46 Comment(1)
Not an error at all; appendChild first removes from the old parent, if any, even if it's the same as the new parent, then adds it to the new parent's child list.Cookery
N
5

Yes the order is what specifies what object will be in front of the other. To manipulate the order you will need to move things about the DOM. There is a good example of this on the SVG wiki at https://www.w3.org/TR/SVG11/render.html#RenderingOrder

Ns answered 27/1, 2009 at 2:49 Comment(1)
That link no longer works. This link explains the same idea: linkMuskrat
K
5

Another solution not mentioned is to put each svg item/set of items into a div. You can alter the z-index of the divs easily.

Fiddle: SVG elements cycles with z-index for containers

...Press on the buttons to 'push' that element to the front. (vs repainting the whole set and pushing 1 element to front, but keeping the original order as in the accepted solution)

It would be very nice to have relative z indices...

stackOverflow wants me to put the code if it's a link from jsfiddle?...ook

var orderArray=[0,1,2];
var divArray = document.querySelectorAll('.shape');
var buttonArray = document.querySelectorAll('.button');

for(var i=0;i<buttonArray.length;i++){
    buttonArray[i].onclick=function(){
        var localI = i;
        return function(){clickHandler(orderArray.indexOf(localI));};
    }();
}


function clickHandler(divIndex) {
     orderArray.push(orderArray.splice(divIndex, 1)[0]);
    orderDivs();
}

function orderDivs(){
    for(var i=0;i<orderArray.length;i++){
       divArray[orderArray[i]].style.zIndex=i;        
    }
}
svg {
    width: 100%;
    height: 100%;
    stroke: black;
    stroke-width:2px;
    pointer-events: all;
}
div{
    position:absolute;
}
div.button{
    top:55%;
    height:5%;
    width:15%;
    border-style: outset;
    cursor:pointer;
    text-align: center;
    background:rgb(175, 175, 234);
    
}
div.button:hover{
    border-style: inset;
    background:yellow;
    
}
div.button.first{
    left:0%;
}
div.button.second{
    left:20%;
}
div.button.third{
    left:40%;
}
<div class="shape">
    <svg>
    <circle cx="50" cy="50" r="40" fill="lime" />
    </svg>
</div>
<div class="shape">
    <svg>
        <rect x="4" y="20" width="200" height="50" fill="cyan" />
    </svg>
</div>
<div class="shape">
    <svg>
        <circle cx="70" cy="70" r="50" fill="fuchsia" />
    </svg>
</div>

<div class='button first'>lime</div>
<div class='button second'>cyan</div>
<div class='button third'>fuchsia</div>
Koa answered 6/3, 2015 at 17:49 Comment(0)
S
4

If you use the svg.js library, you can use the ".front()" command to move any element to the top.

Scurrilous answered 13/3, 2015 at 12:15 Comment(0)
C
3

SVG uses a "painters model" of rendering. Paint is applied in successive operations to the output device such that each operation paints over some area of the output device. When the area overlaps a previously painted area the new paint partially or completely obscures the old.- link to this

Cellist answered 16/1, 2014 at 13:3 Comment(0)
C
3

Adam Bradley's comment to Sam's answer was exactly on point. It uses jQuery rather than CSS only, but he said this:

$('#thing-i-want-on-top').appendTo('#my-svg');

However, for what I was creating, I needed my SVG Path to be above any other path on hover, but still be below my SVG Text. So here's what I came up with:

$('#thing-i-want-on-top').insertBefore('#id-for-svg-text');

Both appendTo and insertBefore will move your element to a new location allowing you to change the depth of the SVG element at will.

Cacophonous answered 26/8, 2016 at 11:54 Comment(0)
R
2

In SVG, to get a higher larger Z index you should move the element down in the DOM tree. You can do this with jQuery, selecting the SVG element, removing it and appending it again at the position you want:

$('g.element').remove().appendTo('svg');
Robot answered 4/10, 2016 at 21:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.