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(); });
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.
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 }); })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With