D3.js Headers in the Power Mode Summary Chart

So, I worked on a force-based collapsible chart based on the following.

I am trying to move from this and add headers to each node. I followed a similar answer here on stackoverflow, but I cannot implement the solution from this answer in the above example and other similar solutions.

Please can someone point me in the right direction.

Before implementation, see the project code below.

enter image description here

Js

var w = 600, h = 600, radius = 10, node, link, root; var force = d3.layout.force() .on("tick", tick) .charge(function(d) { return -500; }) .linkDistance(function(d) { return d.target._children ? 100 : 50; }) .size([w, h - 160]); var svg = d3.select("body").append("svg") .attr("width", w) .attr("height", h); root = words[0]; //set root node root.fixed = true; root.x = w / 2; root.y = h / 2 - 80; update(); function update() { var nodes = flatten(root), links = d3.layout.tree().links(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .start(); // Update the links… link = svg.selectAll(".link") .data(links); // Enter any new links. link.enter().insert("svg:line", ".node") .attr("class", "link") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // Exit any old links. link.exit().remove(); // Update the nodes… node = svg.selectAll("circle.node") .data(nodes) .style("fill", color); node.transition() .attr("r", radius); node.append("title") .text(function(d) { return d.name; }); // Enter any new nodes. node.enter().append("svg:circle") .attr("class", "node") .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) .attr("r", radius) .style("fill", color) .on("click", click) .call(force.drag); // Exit any old nodes. node.exit().remove(); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); } // Color leaf nodes orange, and packages white or blue. function color(d) { if(d._children){ return "#95a5a6"; }else{ switch(d.group) { case 'r': //adverb return "#e74c3c"; break; case 'n': //noun return "#3498db"; break; case 'v': //verb return "#2ecc71"; break; case 's': //adjective return "#e78229"; break; default: return "#9b59b6"; } } } // Toggle children on click. function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(); } // Returns a list of all nodes under the root. function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0); if (!node.id) node.id = ++i; nodes.push(node); return node.size; } root.size = recurse(root); return nodes; } 

CSS

 circle.node { cursor: pointer; stroke: #34495e; stroke-width: 2px; box-sizing: border-box; stroke-location: inside; } line.link { fill: none; stroke: #34495e; stroke-width: 1.5px; } 

HTML

 <!DOCTYPE html> <body> <script src="http://d3js.org/d3.v2.min.js?2.9.6"></script> <script> var words = [ { "group":"n", "word":"main node", "children":[ { "group":"n", "name":"sub node 1" }, { "group":"n", "name":"sub node 2" }, { "group":"n", "name":"sub node 3" }, { "group":"v", "name":"sub node 4" }, { "group":"s", "name":"sub node 5" }, { "group":"s", "name":"sub node 6" }, { "group":"s", "name":"sub node 7" }, { "group":"s", "name":"sub node 8" }, { "group":"s", "name":"sub node 9" }, { "group":"s", "name":"sub node 10" }, { "group":"s", "name":"sub node 11" }, { "group":"r", "name":"sub node 12", "children":[ { "group":"r", "name":"sub sub node 1" }, { "group":"r", "name":"sub sub node 2" }, { "group":"r", "name":"sub sub node 3" } ] } ] } ] </script> </body> 

Jsfiddle

+5
source share
2 answers

Add headers to node as follows.

 title = svg.selectAll("text.title") .data(nodes); title.enter() .append("text") //In your code you used title instead of text .attr("class", "title") .text(function(d) { return d.name; }); title.exit().remove(); 

Note that headers must be added after the nodes of the circle. Or the names may be cropped.

Also update the position of the title in the tick.

  title.attr("transform", function(d){ return "translate("+d.x+","+d.y+")"; }); 

Here is jsfiddle

+3
source

Interestingly, this looks like a piece of code with your code as it is, but only after you have minimized or expanded the node. This should give you some idea of ​​where your problem is. Basically, you add a title element to each node before the nodes actually exist. Move

 node.append("title") .text(function(d) { return d.name; }); 

after

 node.enter().append("svg:circle") .attr("class", "node") .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) .attr("r", radius) .style("fill", color) .on("click", click) .call(force.drag); 

call.

Here is the snippet with the completed change:

 var words = [{ "group": "n", "word": "main node", "children": [{ "group": "n", "name": "sub node 1" }, { "group": "n", "name": "sub node 2" }, { "group": "n", "name": "sub node 3" }, { "group": "v", "name": "sub node 4" }, { "group": "s", "name": "sub node 5" }, { "group": "s", "name": "sub node 6" }, { "group": "s", "name": "sub node 7" }, { "group": "s", "name": "sub node 8" }, { "group": "s", "name": "sub node 9" }, { "group": "s", "name": "sub node 10" }, { "group": "s", "name": "sub node 11" }, { "group": "r", "name": "sub node 12", "children": [{ "group": "r", "name": "sub sub node 1" }, { "group": "r", "name": "sub sub node 2" }, { "group": "r", "name": "sub sub node 3" }] }] }] var w = 600, h = 600, radius = 10, node, link, root; var force = d3.layout.force() .on("tick", tick) .charge(function(d) { return -500; }) .linkDistance(function(d) { return d.target._children ? 100 : 50; }) .size([w, h - 160]); var svg = d3.select("#viz").append("svg") .attr("width", w) .attr("height", h); root = words[0]; //set root node root.fixed = true; root.x = w / 2; root.y = h / 2 - 80; update(); function update() { var nodes = flatten(root), links = d3.layout.tree().links(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .start(); // Update the links… link = svg.selectAll(".link") .data(links); // Enter any new links. link.enter().insert("svg:line", ".node") .attr("class", "link") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // Exit any old links. link.exit().remove(); // Update the nodes… node = svg.selectAll("circle.node") .data(nodes) .style("fill", color); node.transition() .attr("r", radius); // Enter any new nodes. node.enter().append("svg:circle") .attr("class", "node") .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) .attr("r", radius) .style("fill", color) .on("click", click) .call(force.drag); node.append("title") .text(function(d) { return d.name; }); // Exit any old nodes. node.exit().remove(); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); } // Color leaf nodes orange, and packages white or blue. function color(d) { if (d._children) { return "#95a5a6"; } else { switch (d.group) { case 'r': //adverb return "#e74c3c"; break; case 'n': //noun return "#3498db"; break; case 'v': //verb return "#2ecc71"; break; case 's': //adjective return "#e78229"; break; default: return "#9b59b6"; } } } // Toggle children on click. function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(); } // Returns a list of all nodes under the root. function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0); if (!node.id) node.id = ++i; nodes.push(node); return node.size; } root.size = recurse(root); return nodes; } 
 circle.node { cursor: pointer; stroke: #34495e; stroke-width: 2px; box-sizing: border-box; stroke-location: inside; } line.link { fill: none; stroke: #34495e; stroke-width: 1.5px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id="viz"></div> 

Note. This fix has a problem when expanding or dropping a node with an incorrect header. This is due to the way d3 associates data with DOM elements. By default, d3 will bind based on the index. Because adding and removing data can potentially change the order, the index values ​​for the data can change, which means that the relationship between the DOM elements and the data is broken. This can be fixed by explicitly setting the key for data binding. You can use something unique to the key, but for this example, I just used the name property that you defined. For instance:

 var words = [{ "group": "n", "word": "main node", "children": [{ "group": "n", "name": "sub node 1" }, { "group": "n", "name": "sub node 2" }, { "group": "n", "name": "sub node 3" }, { "group": "v", "name": "sub node 4" }, { "group": "s", "name": "sub node 5" }, { "group": "s", "name": "sub node 6" }, { "group": "s", "name": "sub node 7" }, { "group": "s", "name": "sub node 8" }, { "group": "s", "name": "sub node 9" }, { "group": "s", "name": "sub node 10" }, { "group": "s", "name": "sub node 11" }, { "group": "r", "name": "sub node 12", "children": [{ "group": "r", "name": "sub sub node 1" }, { "group": "r", "name": "sub sub node 2" }, { "group": "r", "name": "sub sub node 3" }] }] }] var w = 600, h = 600, radius = 10, node, link, root; var force = d3.layout.force() .on("tick", tick) .charge(function(d) { return -500; }) .linkDistance(function(d) { return d.target._children ? 100 : 50; }) .size([w, h - 160]); var svg = d3.select("#viz").append("svg") .attr("width", w) .attr("height", h); root = words[0]; //set root node root.fixed = true; root.x = w / 2; root.y = h / 2 - 80; update(); function update() { var nodes = flatten(root), links = d3.layout.tree().links(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .start(); // Update the links… link = svg.selectAll(".link") .data(links); // Enter any new links. link.enter().insert("svg:line", ".node") .attr("class", "link") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // Exit any old links. link.exit().remove(); // Update the nodes… node = svg.selectAll("circle.node") .data(nodes, function(d) { return d.name; }) .style("fill", color); node.transition() .attr("r", radius); // Enter any new nodes. node.enter().append("svg:circle") .attr("class", "node") .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) .attr("r", radius) .style("fill", color) .on("click", click) .call(force.drag); node.append("title") .text(function(d) { return d.name; }); // Exit any old nodes. node.exit().remove(); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); } // Color leaf nodes orange, and packages white or blue. function color(d) { if (d._children) { return "#95a5a6"; } else { switch (d.group) { case 'r': //adverb return "#e74c3c"; break; case 'n': //noun return "#3498db"; break; case 'v': //verb return "#2ecc71"; break; case 's': //adjective return "#e78229"; break; default: return "#9b59b6"; } } } // Toggle children on click. function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(); } // Returns a list of all nodes under the root. function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0); if (!node.id) node.id = ++i; nodes.push(node); return node.size; } root.size = recurse(root); return nodes; } 
 circle.node { cursor: pointer; stroke: #34495e; stroke-width: 2px; box-sizing: border-box; stroke-location: inside; } line.link { fill: none; stroke: #34495e; stroke-width: 1.5px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id="viz"></div> 

The difference here is in the call to node.data :

  node = svg.selectAll("circle.node") .data(nodes, function(d) { return d.name; }) .style("fill", color); 

The function in the call .data(nodes, function...) tells d3 to use everything that is returned from the function as a key (see https://github.com/mbostock/d3/wiki/Selections#data for more information. Information ) This ensures that the correct graphic elements are removed, added, and updated when the nodes are collapsed and expanded.

+3
source

Source: https://habr.com/ru/post/1206215/


All Articles