Controlling SVG Draw Order in D3
Tags: javascript, d3, dataviz, svg
Published: 2021-01-26 09:10:45

Sooner or later when working with D3 you'll find yourself in a situation where you need to change how elements overlap each other. Most people (including myself) will first try setting the CSS property `z-index`, but it turns out unfortunately SVG elements don't respect this property.

The order in which SVG elements are drawn determines the order in which they appear (an element drawn last will appear "over" any other elements it overlaps). The draw order of SVG elements follows a simple rule: **elements that appear first are drawn first**.



For example, take the following code snippet:

{% codepen https://codepen.io/_cselig/pen/abmezKR default-tab=js,result %}

The resulting HTML looks something like:
```html
<svg>
  <circle fill="green" r=50 cx=75 cy=100></circle>
  <rect fill="blue" width=80 height=80 x=85 y=60></rect>
</svg>
```

The green circle appears before the blue square, so it is drawn first and appears below.

## D3's `raise` function

If we wanted to make the blue square appear on top, we could just reverse the order that the elements are created in the code. This works fine for this simple example, but when the code gets more complicated (and especially if we're working with abstractions we've written) we want to control draw order without having to change the order of our code.

To this end, we can use D3's `selection.raise()` function:

{% codepen https://codepen.io/_cselig/pen/QWKewXq default-tab=js,result %}

Which results in the HTML:
```html
<svg>
  <rect fill="blue" width=80 height=80 x=85 y=60></rect>
  <circle fill="green" r=50 cx=75 cy=100></circle>
</svg>
```

The green circle now appears after the blue square in the HTML, which means it appears on top of the square in the rendered SVG.

We can further illustrate this by calling `raise()` on element mouseover (try hovering over the shapes below).

{% codepen https://codepen.io/_cselig/pen/KKgOppo
 default-tab=js,result %}

## Ordering and SVG Groups

SVG groups are often used to organize elements in a chart. We can use groups as another way to control draw ordering. In the below example the green circle appears above the blue square despite the fact that is appears before the square in the code.

{% codepen https://codepen.io/_cselig/pen/YzGmXXy
 default-tab=js,result %}

This is because we've added the circle to `group2` and the square to `group1`, and `group1` appears before `group2`. Take a look at the HTML:
```html
<svg>
  <g class="group1">
    <rect fill="blue" width=80 height=80 x=85 y=60></rect>
  </g>
  <g class="group2">
    <circle fill="green" r=50 cx=75 cy=100></circle>
  </g>
</svg>
```

Now however we'll have a problem if we try to bind `raise()` to element mouseover. Notice in the below example that hovering over the shapes does nothing:

{% codepen https://codepen.io/_cselig/pen/eYdqNwO
 default-tab=js,result %}

This is because `raise` only changes the order of the element within its parent group. In this example each group only has one element, making `raise` a no-op.

To make this work we could bind `raise` to the parent group mouseover, but this might be a little weird because the group doesn't necessarily have the same shape as the child element. A clean way to do it would be to have a function that raises the parent group on element mouseover. All we need to change then is our mouseover callback from `d3.select(this).raise()` to `d3.select(this.parentNode).raise()`.

{% codepen https://codepen.io/_cselig/pen/qBaeOEq
 default-tab=js,result %}

## Conclusion

Hopefully these examples help clear up how SVG draw order works! Keeping these simple rules in mind can help you exert finer control over the details when working with complicated visualizations.