1

As long as there is no z-index for SVG, I made my rectangles change z-position with DOM manipulations. However, I have a new problem. For some reason transition stops to work. I suspect it is because of DOM reordering, but I HAVE TO use it like that.

function redZ() {
  document.getElementById('svgField').appendChild(document.getElementById('redRect'));
  document.getElementById('redRect').style.transition = "2s linear";
  document.getElementById('redRect').style.fill = "black";
}

function blueZ() {
  document.getElementById('svgField').appendChild(document.getElementById('blueRect'));
  document.getElementById('blueRect').style.transition = "2s linear";
  document.getElementById('blueRect').style.fill = "yellow";
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
</svg>
Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184
CeeJay
  • 569
  • 2
  • 6
  • 16
  • Depending on the overall structure of your actual SVG, you have the option of splitting the elements in your SVG into separate `` elements, and apply `z-index` on those. – jcaron Oct 28 '17 at 11:57

2 Answers2

0

This seems to be a problem with the redrawing caused by the DOM manipulation. The following is not very elegant, but setting a timeout of a low milliseconds value (in some circumstances you might meed a slightly higher value of 50/100ms) can often solve redrawing-related issues as a last resort.

In addition, you should avoid changing the transition value on every click, that's simply not needed.

document.getElementById('redRect').style.transition = "2s linear";
document.getElementById('blueRect').style.transition = "2s linear";

function redZ() {
  document.getElementById('svgField').appendChild(document.getElementById('redRect'));
  window.setTimeout(function() {
    document.getElementById('redRect').style.fill = "black";
  }, 10);
}

function blueZ() {
  document.getElementById('svgField').appendChild(document.getElementById('blueRect'));
  window.setTimeout(function() {
    document.getElementById('blueRect').style.fill = "yellow";
  }, 10);
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
</svg>
Constantin Groß
  • 10,719
  • 4
  • 24
  • 50
  • 1
    _"This seems to be a problem with the redrawing caused by the DOM manipulation."_ - more exactly, it is a problem with when "control" is handed back over to the rendering engine. _All_ of the DOM and style manipulation is performed as one uninterrupted task. The element is appended to the specified DOM position, and the CSS transition and fill properties are set. Only then is this handed over to the rendering engine, which (for the original `redZ` function example) looks at it and goes, ok, so you have given me an element with a black fill color, [...] – CBroe Oct 28 '17 at 12:14
  • 1
    [...] and I am supposed to transition everything when some property changes. Got it! Lemme check ... nope, fill color still back, everything else still the same - I think we're done here? The slight timeout (more "modern" approaches usually use `requestAnimationFrame`) fixes this, because it hands control over to rendering in between the two steps, too, appending of the element to the DOM, and the change of fill color - so when the RE looks at it again after the fill color change, it now has the change to recognize that change in the first place. – CBroe Oct 28 '17 at 12:14
  • Thanks @CBroe for going into more detail on this than I could. Just a quick note, requestAnimationFrame was what I tried first, but it seems that wasn't enough delay in order to handle this. – Constantin Groß Oct 28 '17 at 12:17
  • 1
    You're right, apparently browsers optimize _against_ that with rAF (https://stackoverflow.com/a/40617982/1427878), so setTimeout is the safer approach here. Good point. – CBroe Oct 28 '17 at 12:20
  • As mentioned in other answers to https://stackoverflow.com/q/24148403/1427878, sometimes explicitly doing something in JS in between that forces it to "ask" the rendering engine for stuff explicitly (like an element's offsetWidth, bounding client rectangle, etc.) can also make this work. – CBroe Oct 28 '17 at 12:23
  • Could the downvoter be bothered to explain their reasons? – Constantin Groß Oct 30 '17 at 09:06
0

Instead of moving the active rect above the other rect in the DOM order, move the other rect below the active rect. This allows the transitions to work as expected.

In other words, when the redRect is clicked, instead of moving the redRect to the bottom of the DOM order (so it's visually on top), move the blueRect to the top of the DOM order (so it's visually on bottom). With:

document.getElementById('svgField').prepend(document.getElementById('blueRect'));

Like this:

function redZ() {
  document.getElementById('svgField').prepend(document.getElementById('blueRect'));
  document.getElementById('redRect').style.transition = "2s linear";
  document.getElementById('redRect').style.fill = "black";
}

function blueZ() {
  document.getElementById('svgField').prepend(document.getElementById('redRect'));
  document.getElementById('blueRect').style.transition = "2s linear";
  document.getElementById('blueRect').style.fill = "yellow";
}
<svg width="180" height="150" id="svgField">
   <rect onclick="redZ()" id="redRect" x="0" y="0" width="100" height="100" fill="red" />
   <rect onclick="blueZ()" id="blueRect" x="60" y="40" width="100" height="100" fill="blue" />
   
</svg>
Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184