Updating Svg Element Z-Index with D3

Updating SVG Element Z-Index With D3

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
});
})

Reorder elements of SVG ( z-index ) in D3.js

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.

How do I change the z-index of a canvas element for DC.JS plot?

It looks like when the canvas scatter plot was implemented, the author had trouble because the canvas element is added after the svg element, which by default puts it forward in the z order — but the svg element needs to be in front in order for brushing to work.

Then the maintainer (I) did not catch this while reviewing the patch for merge, and did not suggest a better mechanism to achieve the goal, since it seemed to work.

You are on the right track with

eventScatterChart.select('canvas').style('z-index', 2000);

It's just that this needs to run after every render and redraw, and you also have to account for the svg layer in order for the brush to work:

  eventScatterChart.on('pretransition', chart => {
chart.select('svg').style('z-index', 1);
chart.select('canvas').style('z-index', 0)
})

Here is a working fork of your stackblitz.

BTW, the folks talking about SVG z-index are completely correct, but this is not an SVG problem, since it has to do with the canvas and svg HTML elements inside the chart div, not any SVG elements.

I filed an issue here. Thanks for raising this!

Z-index in d3.js

SVG does not have a Z order but it draws the objects in the order they are created. You just need to create the path and circle after you create the highlight regions.

Here is the forked fiddle:
http://jsfiddle.net/o00fzgaf/1/

Z index in d3.js

SVG doesn't support Z-Index so the order the elements are appended determine what zindex they are so to speak.

@Softwarenewbie7331 is correct but since your SVG is already determined you have to move the edges your self.

Here is the code to do this (vanilla JS) :

var theGraph = document.getElementById('graph0') //getContainer
var polygon = document.getElementsByTagName('polygon')[0] //getPolygon to insert after

var allEdgesJS = document.getElementsByClassName("edge"); //select all Edges
for (var i = 0; i < allEdgesJS.length; i++) { //Loop through edges to move
theGraph.insertBefore(allEdgesJS[i], polygon.nextSibling); //insert after polygon

}

The above code works by looping through the edges and moving them one by one up the DOM before you create the ellipse's. I use the insertBefore (Documentation Here). To insert before everything in the container. But I customise this to insert after the polygon element by using .nextSibling (Documentation Here).

Here is the updated working fiddle, without all the annoying debuggers you left in :P : https://jsfiddle.net/thatOneGuy/ng0pt0m0/1/

Working code incase fiddle is down :

var edges = d3.selectAll('.edge');var allEllipse = d3.selectAll('ellipse');var allNodes = d3.selectAll('.node');var ellipseSelected;var pathSelected;var parentNodeX;var parentNodeY;var relationshipName;var indexEdge;var fromData;var toData;//flag =1 ,when we have both src and trgvar flag = 1;var data = [{  "seq": "6",  "start": "Delhi",  "end": "",  "rel": "",  "rows": "700"}, {  "seq": "1",  "start": "Mumbai",  "end": "Bangalore",  "rel": " BLR-MUM-440 ",  "rows": "300"}, {  "seq": "2",  "start": "Bangalore",  "end": "Goa",  "rel": " BLR-GOA-432 ",  "rows": "11"}, {  "seq": "3",  "start": "Goa",  "end": "Jaipur",  "rel": " GOA-JAI-884 ",  "rows": "111"}, {  "seq": "4",  "start": "Kolkatta",  "end": "Bangalore",  "rel": " BLR-KOL-228 ",  "rows": "500"}, {  "seq": "5",  "start": "Jaipur",  "end": "Kolkatta",  "rel": " JAI-KOL-743 ",  "rows": "700"}, {  "seq": "6",  "start": "Delhi",  "end": "",  "rel": "",  "rows": "999"}];//debugger; 
var theGraph = document.getElementById('graph0')var polygon = document.getElementsByTagName('polygon')[0]
var allEdgesJS = document.getElementsByClassName("edge"); // order: first, second, thirdfor (var i = 0; i < allEdgesJS.length; i++) { theGraph.insertBefore(allEdgesJS[i], polygon.nextSibling); // order: third, first, second
}

function ellipseAdd() {

//console.log("ellipse added"); // debugger d3.select(ellipseSelected.parentNode) .append("ellipse") .attr('cx', parentNodeX) //thisParentBBox.left + thisParentBBox.width/2) .attr('cy', parentNodeY) .attr("rx", 15) .attr("ry", 7) .attr("stroke-width", 2) .attr("stroke", "white") .style('fill', '#AB4B52') d3.select(ellipseSelected.parentNode) .data([toData]) .append("text") .attr('x', parentNodeX - 10) //thisParentBBox.left + thisParentBBox.width/2) .attr('y', parentNodeY + 4).text(0).style('fill', 'white') .attr("font-size", "8px") .transition() .duration(3000) .tween("text", function(d) { //debugger var i = d3.interpolate(fromData, d), prec = (d + "").split("."), round = (prec.length > 1) ? Math.pow(10, prec[1].length) : 1; //debugger return function(t) {
this.textContent = Math.round(i(t) * round) / round; }; });

}
function blinker() {

if (flag == 0) { //for adding ellipse and text to it ellipseAdd();
//console.log("ellipse added done"); } else { //blink 3 things\ //ellipse ellipseAdd(); //edgeTransition //console.log("ellipse added done now blinking"); //console.log("blinkStartEdge" +indexEdge ); d3.select('#' + indexAndEdge[indexEdge].id + ' path') .transition().style('stroke', 'grey').duration(300).style('opacity', 1) .transition().style('stroke', 'red').style('stroke-width', 2) .transition().duration(300).duration(300).style('opacity', 1) .transition().style('stroke', 'grey').duration(300).style('opacity', 1) .transition().style('stroke', 'red').style('stroke-width', 2) .transition().duration(300).duration(300).style('opacity', 1) .transition().style('stroke', 'grey').duration(300).style('opacity', 1) .transition().style('stroke', 'red').style('stroke-width', 2) .transition().duration(300).duration(300).style('opacity', 1) .transition().style('stroke', 'grey').style('stroke-width', 1).duration(300).style('opacity', 1) .transition().style('stroke', 'yellow').style('stroke-width', 1); //select current id from array
}

}

edges.style('opacity', 1);var indexAndEdge = [];var countOnNode = [];edges.each(function(d, i) { // debugger var thisEdgeCount = this.id.substring(4); indexAndEdge.push({ //push index you are at, the edge count worked out above and the id index: i, count: thisEdgeCount, id: this.id, //destination:String(this.childNodes[1].childNodes[0].nodeValue).split("->")[1], relation: this.childNodes[7].childNodes[0]

})});
d3.selectAll('.node').each(function(d, i) { //debugger; var thisNodeCount = this.id; countOnNode.push({ //push index you are at, the edge count worked out above and the id id: thisNodeCount, prevData: 0, incrementData: 0

})});
//debugger;//for(var i=0 ; i< data.length ;i++)

var newCount = 0
function timer() { setTimeout(function(d) {
if (newCount < data.length) { //if we havent gone through all edges
// debugger

if (data[newCount].end.length == 0) { flag = 0; for (jj = 0; jj < allNodes[0].length; jj++) { //if sourseName matches // debugger; if (String(allNodes[0][jj].childNodes[5].childNodes[0].nodeValue) == data[newCount].start) { //console.log("yesss"); ellipseSelected = d3.selectAll('.node')[0][jj].childNodes[3];
parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value); parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2); //send the data to interpolate //match id and update prevData ,incrementData for (var l = 0; l < countOnNode.length; l++) { if (countOnNode[l].id == d3.selectAll('.node')[0][jj].id) { countOnNode[l].prevData = countOnNode[l].incrementData; countOnNode[l].incrementData = data[newCount].rows; fromData = countOnNode[l].prevData; toData = countOnNode[l].incrementData; } }
blinker();
} } flag = 1; } else { //check relation and targetNode //check target flag = 1; for (var j = 0; j < allNodes[0].length; j++) { if (String(allNodes[0][j].childNodes[5].childNodes[0].nodeValue) == data[newCount].end) { ellipseSelected = d3.selectAll('.node')[0][j].childNodes[3]; parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value); parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2);
for (var l = 0; l < countOnNode.length; l++) { if (countOnNode[l].id == d3.selectAll('.node')[0][j].id) { countOnNode[l].prevData = countOnNode[l].incrementData; countOnNode[l].incrementData = data[newCount].rows; fromData = countOnNode[l].prevData; toData = countOnNode[l].incrementData; } } //set the edge by checking relation for (var k = 0; k < indexAndEdge.length; k++) {
//if(edges[0][k].childNodes[7].childNodes[0] == indexAndEdge) if (data[newCount].rel == String(indexAndEdge[k].relation.nodeValue)) { indexEdge = k; } } blinker(); flag = 0; } }
}

//allEllipse
newCount++; timer(); } else { // count =0 ; timer() //console.log('end') //end } }, 3000)}

timer();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script><?xml version="1.0" encoding="UTF-8" standalone="no"?>  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">  <!-- Generated by graphviz version 2.38.0 (20140413.2041) -->  <!-- Title: graphname Pages: 1 -->

<svg width="708pt" height="305pt" viewBox="0.00 0.00 707.95 305.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 301)"> <title>graphname</title> <polygon fill="white" stroke="none" points="-4,4 -4,-301 703.946,-301 703.946,4 -4,4" /> <!-- 0 --> <g id="node1" class="node"> <title>0</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="456.946" cy="-192" rx="40.0939" ry="18" /> <text text-anchor="middle" x="456.946" y="-188.3" font-family="Times New Roman,serif" font-size="14.00">Mumbai</text> </g> <!-- 1 --> <g id="node2" class="node"> <title>1</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="399.946" cy="-18" rx="46.5926" ry="18" /> <text text-anchor="middle" x="399.946" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Bangalore</text> </g> <!-- 0->1 --> <g id="edge1" class="edge"> <title>0->1</title> <path fill="none" stroke="grey" d="M460.066,-163.558C462.067,-134.55 461.467,-88.3313 442.946,-54 438.559,-45.8673 431.415,-38.9123 424.2,-33.3953" /> <polygon fill="grey" stroke="grey" points="456.557,-163.514 459.204,-173.773 463.532,-164.103 456.557,-163.514" /> <text text-anchor="middle" x="509.946" y="-101.3" font-family="Times New Roman,serif" font-size="14.00"> BLR-MUM-440 </text> </g> <!-- 6 --> <g id="node7" class="node"> <title>6</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="339.946" cy="-105" rx="27" ry="18" /> <text text-anchor="middle" x="339.946" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Goa</text> </g> <!-- 0->6 --> <g id="edge6" class="edge"> <title>0->6</title> <path fill="none" stroke="grey" d="M406.617,-188.92C382.78,-185.106 356.24,-176.165 340.946,-156 333.916,-146.73 333.971,-133.465 335.621,-122.928" /> <polygon fill="grey" stroke="grey" points="406.251,-192.402 416.627,-190.258 407.179,-185.464 406.251,-192.402" /> <text text-anchor="middle" x="391.946" y="-144.8" font-family="Times New Roman,serif" font-size="14.00"> GOA-MUM-108 </text> </g> <!-- 2 --> <g id="node3" class="node"> <title>2</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="118.946" cy="-105" rx="37.0935" ry="18" /> <text text-anchor="middle" x="118.946" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Cochin</text> </g> <!-- 3 --> <g id="node4" class="node"> <title>3</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="118.946" cy="-192" rx="32.4942" ry="18" /> <text text-anchor="middle" x="118.946" y="-188.3" font-family="Times New Roman,serif" font-size="14.00">Jaipur</text> </g> <!-- 3->2 --> <g id="edge8" class="edge"> <title>3->2</title> <path fill="none" stroke="grey" d="M118.946,-163.734C118.946,-150.419 118.946,-134.806 118.946,-123.175" /> <polygon fill="grey" stroke="grey" points="115.446,-163.799 118.946,-173.799 122.446,-163.799 115.446,-163.799" /> <text text-anchor="middle" x="162.446" y="-144.8" font-family="Times New Roman,serif" font-size="14.00"> COC-JAI-983 </text> </g> <!-- 3->6 --> <g id="edge5" class="edge"> <title>3->6</title> <path fill="none" stroke="grey" d="M154.314,-176.995C170.385,-170.615 189.636,-162.951 206.946,-156 223.413,-149.388 227.386,-147.373 243.946,-141 268.615,-131.506 297.089,-121.207 316.581,-114.256" /> <polygon fill="grey" stroke="grey" points="152.768,-173.843 144.763,-180.784 155.349,-180.35 152.768,-173.843" /> <text text-anchor="middle" x="287.946" y="-144.8" font-family="Times New Roman,serif" font-size="14.00"> GOA-JAI-884 </text> </g> <!-- 4 --> <g id="node5" class="node"> <title>4</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="40.9464" cy="-279" rx="40.8928" ry="18" /> <text text-anchor="middle" x="40.9464" y="-275.3" font-family="Times New Roman,serif" font-size="14.00">Kolkatta</text> </g> <!-- 4->1 --> <g id="edge3" class="edge"> <title>4->1</title> <path fill="none" stroke="grey" d="M29.5176,-251.287C19.1406,-222.958 7.48741,-177.368 21.9464,-141 34.1426,-110.324 44.1274,-103.101 72.9464,-87 163.431,-36.4467 286.981,-23.3374 353.307,-20.029" /> <polygon fill="grey" stroke="grey" points="26.3613,-252.829 33.2323,-260.894 32.8903,-250.305 26.3613,-252.829" /> <text text-anchor="middle" x="68.4464" y="-144.8" font-family="Times New Roman,serif" font-size="14.00"> BLR-KOL-228 </text> </g> <!-- 4->3 --> <g id="edge7" class="edge"> <title>4->3</title> <path fill="none" stroke="grey" d="M62.7013,-254.293C76.1886,-239.595 93.1216,-221.142 104.866,-208.344" /> <polygon fill="grey" stroke="grey" points="59.8,-252.278 55.6176,-262.012 64.9576,-257.011 59.8,-252.278" /> <text text-anchor="middle" x="127.446" y="-231.8" font-family="Times New Roman,serif" font-size="14.00"> JAI-KOL-743 </text> </g> <!-- 5 --> <g id="node6" class="node"> <title>5</title> <ellipse fill="#b2dfee" stroke="#b2dfee" cx="522.946" cy="-279" rx="29.4969" ry="18" /> <text text-anchor="middle" x="522.946" y="-275.3" font-family="Times New Roman,serif" font-size="14.00">Delhi</text> </g> <!-- 5->0 --> <g id="edge4" class="edge"> <title>5->0</title> <path fill="none" stroke="grey" d="M492.042,-261.717C484.599,-256.574 477.27,-250.295 471.946,-243 464.895,-233.337 461.127,-220.345 459.132,-210.027" /> <polygon fill="grey" stroke="grey" points="490.4,-264.82 500.718,-267.226 494.152,-258.911 490.4,-264.82" /> <text text-anchor="middle" x="520.446" y="-231.8" font-family="Times New Roman,serif" font-size="14.00"> MUM-DEL-340 </text> </g> <!-- 5->1 --> <g id="edge9" class="edge"> <title>5->1</title> <path fill="none" stroke="grey" d="M553.607,-261.01C559.788,-256.059 565.453,-250.047 568.946,-243 587.287,-205.999 727.222,-322.519 562.946,-87 535.932,-48.2704 482.434,-31.5297 444.09,-24.3379" /> <polygon fill="grey" stroke="grey" points="551.149,-258.463 545.015,-267.101 555.197,-264.173 551.149,-258.463" /> <text text-anchor="middle" x="654.446" y="-144.8" font-family="Times New Roman,serif" font-size="14.00"> BLR-DEL-270 </text> </g> <!-- 6->1 --> <g id="edge2" class="edge"> <title>6->1</title> <path fill="none" stroke="grey" d="M340.326,-76.4424C341.465,-68.7133 343.716,-60.6211 347.946,-54 353.629,-45.1046 362.471,-37.9253 371.263,-32.4387" /> <polygon fill="grey" stroke="grey" points="336.824,-76.3034 339.381,-86.5849 343.794,-76.9527 336.824,-76.3034" /> <text text-anchor="middle" x="395.446" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> BLR-GOA-432 </text> </g> </g>
</svg>

How to manipulate the Z-index of an SVG

Try like this:

var svg = d3
.select("body")
.append("svg")
.attr("width", 600)
.attr("height", 600)
.append("g")
.attr("transform", "translate(50,50)");

//tree data
var data = [
{ child: "John", parent: "" },
{ child: "Aron", parent: "Kevin" },
{ child: "Kevin", parent: "John" },
{ child: "Hannah", parent: "Anna" },
{ child: "Rose", parent: "Sarah" },
{ child: "Anna", parent: "John" },
{ child: "Sarah", parent: "Kevin" },
{ child: "Mark", parent: "Anna" },
{ child: "Angle", parent: "Sarah" },
];

//to construct
var dataStructure = d3
.stratify()
.id(function (d) {
return d.child;
})
.parentId(function (d) {
return d.parent;
})(data);

//to define the size of the structure tree
var treeStructure = d3.tree().size([500, 300]);
var information = treeStructure(dataStructure);

//to make the connections curves
var connections = svg.append("g").selectAll("path").data(information.links());
connections
.enter()
.append("path")
.attr("d", function (d) {
return (
"M" +
d.source.x +
"," +
d.source.y +
" C " +
d.source.x +
"," +
(d.source.y + d.target.y) / 2 +
" " +
d.target.x +
"," +
(d.source.y + d.target.y) / 2 +
" " +
d.target.x +
"," +
d.target.y
);
});

//creating the circles with data info
var circles = svg
.append("g")
.selectAll("circle")
.data(information.descendants());

//placing the circles
circles
.enter()
.append("circle")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
.attr("r", 20);
circle {
fill: rgb(88, 147, 0);
}
path {
fill: none;
stroke: black;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://d3js.org/d3.v6.min.js"></script>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<script src="script.js"></script>
</body>
</html>

With JavaScript, can I change the Z index/layer of an SVG g element?

Z index in SVG is defined by the order the elements appear in the document (subsequent elements are painted on top of previous elements).

You will have to change the element order if you want to bring a specific shape to the top.

Updating only those SVG elements where the underlying bound data has been modified

If a single datum attribute can be checked to see if the bound data has changed, one method would be to track that attribute as a property using selection.property and a custom property such as type. When appending the data you could define the property fairly easily:

  .append("circle")
.property("type",function(d) { return d.type; });

Then, when updating, you could filter based on which data are matching or not matching the property:

  circles.data(newdata)
.filter(function(d) {
return d.type != d3.select(this).property("type")
})

This filter will return those elements that have changed their type. Now we can re-assign the property to reflect the new type and transition those filtered elements.

The snippet below should demonstrate this, the datum is just a number one or two (represented by blue and orange), and is used to set the property type. Click on the svg to update the data, only those circles which change their datum will temporarily change their radius, while also changing their color to reflect their new datum.

var svg = d3.select("body")  .append("svg")  .attr("width",400)  .attr("height",400);  var circles = svg.selectAll("circle")  .data(data())  .enter("circle")  .append("circle")  .attr("cy",function(d,i) {    return Math.floor(i/5) * 40 + 20;  })  .attr("cx", function(d,i) {    return i%5 * 40 + 20  })  .attr("r", 8)  .attr("fill",function(d) { return (d) ? "steelblue" : "orange"})  .property("type",function(d) { return d; });  // update on click:svg.on("click", function() {  circles.data(data())    .filter(function(d) {      return d != d3.select(this).property("type") // filter out unchanged data    })    .property("type",function(d) { return d; })  // update to reflect new data    .transition()    .attr("r", 20)    .attr("fill","crimson")    .duration(500)    .transition()    .attr("fill",function(d) { return (d) ? "steelblue" : "orange" })    .attr("r",8)    .duration(500);})  
function data() { var output = []; d3.range(20).map(function(d) { output.push(Math.round(Math.random())); }) return output;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


Related Topics



Leave a reply



Submit