68

How can I reorder Raphael or their underlying SVG elements after creation. Better yet, do something like layers exist in SVG?

Ideally I would like two or more layers in which to place elements at any time; A background and a foreground layer. If that is not an option, popping elements to the front would be ok, and pushing it to the back would be better in this particular case.

Thanks,

Miles
  • 1,357
  • 2
  • 12
  • 20
  • 3
    Whoops, Raphael even has toFront and toBack methods. Silly of me for missing that. Thanks for your answers below as well, I may end up using them anyway – Miles Jul 06 '11 at 07:10

6 Answers6

95

Gimme the Code!

// move element "on top of" all others within the same grouping
el.parentNode.appendChild(el); 

// move element "underneath" all others within the same grouping
el.parentNode.insertBefore(el,el.parentNode.firstChild);

// move element "on top of" all others in the entire document
el.ownerSVGElement.appendChild(el); 

// move element "underneath" all others in the entire document
el.ownerSVGElement.appendChild(el,el.ownerSVGElement.firstChild); 

Within Raphael specifically, it's even easier by using toBack() and toFront():

raphElement.toBack()  // Move this element below/behind all others
raphElement.toFront() // Move this element above/in front of all others

Details

SVG uses a "painters model" when drawing objects: items that appear later in the document are drawn after (on top of) elements that appear earlier in the document. To change the layering of items, you must re-order the elements in the DOM, using appendChild or insertBefore or the like.

You can see an example of this here: http://phrogz.net/SVG/drag_under_transformation.xhtml

  1. Drag the red and blue objects so that they overlap.
  2. Click on each object and watch it pop to the top. (The yellow circles are intended to always be visible, however.)

The re-ordering of elements on this example is done by lines 93/94 of the source code:

el.addEventListener('mousedown',function(e){
  el.parentNode.appendChild(el); // move to top
  ...
},false);

When the mouse is pushed down on an element, it is moved to be the last element of all its siblings, causing it to draw last, "on top" of all others.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
40

If you're using Raphael, popping elements backwards and forwards is very straightforward:

element.toBack()
element.toFront()

Here's the relevant documentation.

Herman Schaaf
  • 46,821
  • 21
  • 100
  • 139
4

There's no z-index in SVG, objects that are later in the code appear above the first ones. So for your needs, you can move the node to the start of the tree or the end of it.

<g> (group) element is a generic container in svg, so they can be layers for you. Just move nodes between the groups to achieve what you need.

Spadar Shut
  • 15,111
  • 5
  • 47
  • 54
  • 2
    But what if I want layering to be a separate concept from grouping? For example, i have two body part shapes, one for the corpus, and another one for an arm. Both parts consist of two separate shapes: one for stroke contour, the other one for fill. And they need to be layered this way: {arm fill} {body fill} {arm stroke} {body stroke} to make it mesh together without overlapping their contours (the contour need to outline them both smoothly). But I need to group arm fill with arm stroke and body fill with body stroke for easier transformations of them :/ How to separate grouping from viewing? – SasQ Nov 28 '12 at 21:07
0

If you predefine your graphic objects, you can then layer them in different orders as time evolves:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 1000" width="400" height="400">
  <rect x="0" y="0" width="1000" height="1000" fill="black"/>
  <defs>
    <rect id="a1" x="0"   y="0"   width="500" height="500" fill="red"/>
    <rect id="a2" x="100" y="100" width="500" height="500" fill="blue"/>
  </defs>
  <g> 
    <use xlink:href="#a1"/>
    <use xlink:href="#a2"/>
    <set attributeName="visibility"  to="hidden" begin="2s"/> 
  </g>
  <g visibility="hidden"> 
    <use xlink:href="#a2"/>
    <use xlink:href="#a1"/>
    <set attributeName="visibility"  to="visible" begin="2s"/> 
  </g>
</svg>
0

I haven't tried it yet but SVG render order looks like it could be a solution. And also 'z-index' is coming, it is already in proposal

sgimeno
  • 1,883
  • 1
  • 22
  • 35
-2

Actually, I think appendChild() alone will copy the element and not just move it. This may not be a problem but if you want to avoid duplicates I suggest something like this:

el.parentNode.appendChild(el.parentNode.removeChild(el));
FloydATC
  • 13
  • 1
  • 2
    Not necessary. appendChild moves, not duplicates the node. see https://developer.mozilla.org/en-US/docs/Web/API/Node.appendChild – Paul Gobée Oct 12 '14 at 14:53
  • 1
    This answer is wrong and should be removed. Because appendChild moves. – Michelle Jan 12 '16 at 02:15
  • 1
    Why don't you (or anyone else) downvote this answer then?! – Balder Jan 12 '16 at 08:31
  • I downvoted, but this is actually a helpful answer for anyone who might think that append copies the elem, and then reads the comments. – chaimp May 18 '16 at 18:51