12

I realise this question has been asked before but I can't get to the bottom of it.

Here is my chart... http://www.gogeye.com/financialnews/piechart/index3.html

All I want to do is have the coin render behind the graph. I know D3 renders in order they are appended.

I have tried to re-append the coin but can't seem to get it working.

I've tried reordering when things are appended in the DOM but keep getting errors probably because variables are getting called before being defined etc.

Can someone give me an example of how to fix this with my code? I don't want you to do the work for me but I've been pulling my hair out for so long, I can't seem to apply other peoples examples to mine.

thanks

bboybeatle
  • 549
  • 1
  • 8
  • 28
  • Can you show us your code? – Armand Jun 04 '14 at 19:23
  • its all on the link... view-source:http://www.gogeye.com/financialnews/piechart/index3.html should i put it in the post? – bboybeatle Jun 04 '14 at 19:25
  • Maybe you could describe your problem a little more clearly. You say "I know D3 renders in order they are appended" (Actually, it's nothing to do with D3 but rather SVG, but close enough.) That being the case, what's not working. Are you not able to figure out how to append content in the desired order? Are you appending it in the right order but it's not rendering correctly? Are you trying to swap the order of existing content? ... – Stephen Thomas Jun 04 '14 at 19:31
  • Sorry, I'm fairly new to javascript and completely new to D3. I think the coin is appending after the graph, and therefore in front of it. I would like to know how to target and re-append that coin afterwards. Or if there is something I've done wrong that will not allow that, maybe I can try and append the coin first from the get-go but having real trouble with that. Maybe I'll try copying it to a new file and swap everything around. My first attempts at that were pretty catastrophic though – bboybeatle Jun 04 '14 at 19:50

2 Answers2

40

I would recommend creating some "layers" using svg g elements which stands for "group".

When you render your chart, you can first define your layers:

var layer1 = svg.append('g');
var layer2 = svg.append('g');
var layer3 = svg.append('g');
// etc... for however many layers you need

Then when you append new elements, you can decide which layer you want them to be on, and it won't matter what order you assign them in, because the group elements have already been added to the DOM, and are ordered. For example:

var layer1 = svg.append('g');
var layer2 = svg.append('g');

var redCircle = layer2.append('circle')
    .attr('cx', 50)
    .attr('cy', 50)
    .attr('r', 16)
    .attr('fill', 'red')

var blueSquare = layer1.append('rect')
    .attr('x', 25)
    .attr('y', 25)
    .attr('width', 50)
    .attr('height', 50)
    .attr('fill', 'blue');

In this case the red circle will be visible above the blue square even though the blue square was created last. This is because the circle and the square are children of different group elements, which are in a pre-defined order.

Here's a FIDDLE of the above example so you can see it in action.

Doing this should take a lot of the guesswork out of when to add certain elements to your chart, and it also helps to organize your elements into a more logical arrangement. Hope that helps, and good luck.

jshanley
  • 9,048
  • 2
  • 37
  • 44
  • 6
    Would it be possible to change which layer is at the top and which is at the bottom programmatically? – gaitat Jul 30 '15 at 20:10
  • Nice. This is a very elegant and readable way to accomplish exactly what's being asked. – Adam Oct 27 '16 at 14:30
7

I am using the D3.js, and found that it has a built-in function for changing the z-order of SVG elements programmatically after the original drawing.

RipTutorial: svg--the-drawing-order covers the d3 builtin function

Quotes from this link:

selection.raise(): Re-inserts each selected element, in order, as the last child of its parent. selection.lower(): Re-inserts each selected element, in order, as the first child of its parent.

d3.selectAll("circle").on("mouseenter", function(){
    d3.select(this).raise(); 
});

d3.selectAll("circle").on("mouseleave", function(){
    d3.select(this).lower(); 
});

see live example their jsFiddle

NicoWheat
  • 2,157
  • 2
  • 26
  • 34