Home > Software engineering >  How to reverse the order of svg elements
How to reverse the order of svg elements

Time:04-09

I am working with a svg element which is following

<svg  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150">
    <rect  id="bg" width="150" height="150" fill="#e6e6e6"></rect>
    <circle  id="circ0" cx="75" cy="75" r="72" fill="none" stroke="blue" stroke-linecap="round" stroke-linejoin="round"></circle>
    <circle  id="circ1" cx="75" cy="75" r="69" fill="none" stroke="green" stroke-linecap="round" stroke-linejoin="round"></circle>
    <circle  id="circ2" cx="75" cy="75" r="66" fill="none" stroke="red" stroke-linecap="round" stroke-linejoin="round"></circle>
    <script href="index.js"></script>  
</svg>

I want to reverse the order of these circles with javascript, which I am currently doing by this way

const svg = document.querySelector("svg");

var x = document.querySelectorAll("[class^='circ']");
var bucket = [];
x.forEach((a, i) => {
    bucket.push(a)
});

bucket.reverse();

x.forEach(
    (a, i) => a.parentNode.removeChild(a)
);

bucket.forEach(
    (a, i) => {
        a.setAttribute("class", 'circ'   [i]);
        a.setAttribute("id", "circ"   [i]);
        svg.appendChild(a);
    }
)
<svg  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150">
    <rect  id="bg" width="150" height="150" fill="#e6e6e6"></rect>
    <circle  id="circ0" cx="75" cy="75" r="72" fill="none" stroke="blue" stroke-linecap="round" stroke-linejoin="round"></circle>
    <circle  id="circ1" cx="75" cy="75" r="69" fill="none" stroke="green" stroke-linecap="round" stroke-linejoin="round"></circle>
    <circle  id="circ2" cx="75" cy="75" r="66" fill="none" stroke="red" stroke-linecap="round" stroke-linejoin="round"></circle>
    <script href="index.js"></script>  
</svg>

It gives me this Snap

Is there a better way of doing this?

CodePudding user response:

append(child) by itself moves DOM Nodes. So your code can be simplified.

But for complexer SVG you probably want to swap DOM positions, because there could be other Elements in between you don't want to affect.

  • Hold CTRL key to see what happens with append
  • Click to see the swapping version,
    a matter of processing an Array and swapping the first with the last element.
  • Note: append was not available in Internet Explorer, that is why you see most posts using appendChild.
    Modern browsers have loads more DOM goodies: replaceWith after , before etc.

<svg viewBox="0 0 10 10" style="height:200px">
  <style>
    text { font-size: 2px }
    [y="3"]{ fill:yellow }
    .first { stroke: black; stroke-width: 0.5 }
  </style>
  <rect  id="bg" width="10" height="10" fill="grey"></rect>
  <circle  id="c0" cx="2" cy="5" r="2" fill="red" />
  <text x="0" y="3">R</text>
  <circle  id="c1" cx="4" cy="5" r="3" fill="green" />
  <text x="1" y="3">G</text>
  <circle  id="c2" cx="6" cy="5" r="4" fill="blue" />
  <text x="2" y="3">B</text>
  <text x="1" y="6">Click Me!</text>
</svg>
<script>
  let svg = document.querySelector("svg");
  function append() {
    [...svg.querySelectorAll("circle")]
    .reverse().forEach((c, i) => {
                    c.parentNode.append(c);
                    c.setAttribute("class", c.id = 'c'   i);
                    });
  }
  function swap() {
    function swapElements(e1, e2) {
      let {id,previousSibling,className:{baseVal:c2}} = e2;
      e1.after(e2); // put e2 after e1
      e2.id = e1.id; e2.setAttribute("class", e1.getAttribute("class"));
      previousSibling.after(e1); // put e1 after where e2 WAS
      e1.id = id;    e1.setAttribute("class", c2);
    }
    let circles = [...svg.querySelectorAll("circle")];
    while (circles.length) {
      let c1 = circles.shift();
      if (circles.length) swapElements(c1, circles.pop())
    }
  }
  svg.onclick = (e) => (e.ctrlKey && append()) || swap();
</script>

  • Related