Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating SVG Element Z-Index With D3

What is an effective way to bring an SVG element to the top of the z-order, using the D3 library?

My specific scenario is a pie chart which highlights (by adding a stroke to the path) when the mouse is over a given piece. The code block for generating my chart is below:

svg.selectAll("path")     .data(d)   .enter().append("path")     .attr("d", arc)     .attr("class", "arc")     .attr("fill", function(d) { return color(d.name); })     .attr("stroke", "#fff")     .attr("stroke-width", 0)     .on("mouseover", function(d) {         d3.select(this)             .attr("stroke-width", 2)             .classed("top", true);             //.style("z-index", 1);     })     .on("mouseout", function(d) {         d3.select(this)             .attr("stroke-width", 0)             .classed("top", false);             //.style("z-index", -1);     }); 

I've tried a few options, but no luck so far. Using style("z-index") and calling classed both did not work.

The "top" class is defined as follows in my CSS:

.top {     fill: red;     z-index: 100; } 

The fill statement is there to make sure I knew it was turning on/off correctly. It is.

I've heard using sort is an option, but I'm unclear on how it would be implemented for bringing the "selected" element to the top.

UPDATE:

I fixed my particular situation with the following code, which adds a new arc to the SVG on the mouseover event to show a highlight.

svg.selectAll("path")     .data(d)   .enter().append("path")     .attr("d", arc)     .attr("class", "arc")     .style("fill", function(d) { return color(d.name); })     .style("stroke", "#fff")     .style("stroke-width", 0)     .on("mouseover", function(d) {         svg.append("path")           .attr("d", d3.select(this).attr("d"))           .attr("id", "arcSelection")           .style("fill", "none")           .style("stroke", "#fff")           .style("stroke-width", 2);     })     .on("mouseout", function(d) {         d3.select("#arcSelection").remove();     }); 
like image 532
Nicholas Pappas Avatar asked Nov 27 '12 23:11

Nicholas Pappas


2 Answers

As explained in the other answers, SVG does not have a notion of a z-index. Instead, the order of elements in the document determines the order in the drawing.

Apart from reordering the elements manually, there is another way for certain situations:

Working with D3 you often have certain types of elements that should always be drawn on top of other types of elements.

For example, when laying out graphs, links should always be placed below nodes. More generally, some background elements usually need to be placed below everything else, while some highlights and overlays should be placed above.

If you have this kind of situation, I found that creating parent group elements for those groups of elements is the best way to go. In SVG, you can use the g element for that. For example, if you have links that should be always placed below nodes, do the following:

svg.append("g").attr("id", "links") svg.append("g").attr("id", "nodes") 

Now, when you paint your links and nodes, select as follows (the selectors starting with # reference the element id):

svg.select("#links").selectAll(".link") // add data, attach elements and so on  svg.select("#nodes").selectAll(".node") // add data, attach elements and so on 

Now, all links will always be appended structurally before all node elements. Thus, the SVG will show all links below all nodes, no matter how often and in what order you add or remove elements. Of course, all elements of the same type (i.e. within the same container) will still be subject to the order in which they were added.

like image 98
notan3xit Avatar answered Oct 04 '22 03:10

notan3xit


One of the solutions presented by the developer is: "use D3's sort operator to reorder the elements." (see https://github.com/mbostock/d3/issues/252)

In this light, one might sort the elements by comparing their data, or positions if they were dataless elements:

.on("mouseover", function(d) {     svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's       if (a.id != d.id) return -1;               // a is not the hovered element, send "a" to the back       else return 1;                             // a is the hovered element, bring "a" to the front   }); }) 
like image 28
futurend Avatar answered Oct 04 '22 01:10

futurend