D3js formatting with hide / unhide on node click changes nodes after extension

I am trying to create a graph using d3 and formatting. To get started, I used the following example http://bl.ocks.org/mbostock/1062288 :

enter image description here

I also need images and labels, so I looked at this http://bl.ocks.org/mbostock/950642 example to understand how I can add them.

My graph will also increase depending on user interaction with nodes, so if a user clicks on a node that has no children, the ajax request is sent to the server service to request more nodes. The graph is intended to be used as a relationship discovery application. I created the following jsfiddle http://jsfiddle.net/R2JGa/7/ to get an idea of ​​what I'm trying to achieve.

It works relatively well, but I have one unpleasant problem: when adding new nodes to the graph, the old nodes are somehow lost. For example, I start with 3 nodes, the root node is a torch. The remaining 2 nodes are "animation" and "analytics". In my example, children will always be "x", "y", "z", "t" anytime you click on a node that currently has no children. After expanding several nodes, you will see that the "animation" or "analytics" are not associated with the root node "flash", but with some other nodes (x, y, z, t). Or sometimes, if you extend x or y or z or t, the child nodes have a duplicate of x or y or z or t. If you press the “flash” to hide the entire graph and then open the “torch” again, you will see that the nodes are correctly connected and named.

I don't seem to understand why this is happening. Can anyone shed some light here? I'm still new to d3 and find it really interesting, but these problems are so annoying ...

Here is the code:

var w = 960, h = 800, node, link, root; var force = d3.layout.force() .charge(-1000) .size([w, h]); var vis = d3.select("#chart").append("svg:svg") .attr("width", w) .attr("height", h); d3.json("data.json", function (json) { root = json; update(); }); function update() { var nodes = flatten(root); nodes.reverse(); nodes = nodes.sort(function (a, b) { return a.index - b.index; }); var links = d3.layout.tree().links(nodes); console.log(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .linkDistance(55) .start(); var link = vis.selectAll(".link") .data(links); link.enter().append("line") .attr("class", "link"); link.exit().remove(); var node = vis.selectAll("g.node") .data(nodes) var groups = node.enter().append("g") .attr("class", "node") .attr("id", function (d) { return d.id }) .on('click', click) .call(force.drag); groups.append("image") .attr("xlink:href", "https://github.com/favicon.ico") .attr("x", -8) .attr("y", -8) .attr("width", 16) .attr("height", 16); groups.append("text") .attr("dx", 12) .attr("dy", "0.35em") .style("font-size", "10px") .text(function (d) { console.log(d); return d.name }); node.exit().remove(); force.on("tick", function () { 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("transform", function (d) { return "translate(" + dx + "," + dy + ")"; }); }); } // Color leaf nodes orange, and packages white or blue. function color(d) { return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; } // Toggle children on click. function click(d) { console.log(d); if (d.children) { d._children = d.children; d.children = null; update(); } else if (d._children) { d.children = d._children; d._children = null; update(); } else { d3.json("expand.json", function (json) { d.children = json.children; update(); }) } } // Returns a list of all nodes under the root. function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.children.forEach(recurse); if (!node.id) node.id = ++i; nodes.push(node); } recurse(root); return nodes; } 

And here are 2 json files that I request:

data.json

 { "name": "flare", "id" : "flare", "children": [ { "name": "analytics", "id": "analytics" }, { "name": "animate", "id": "animate" } ] } 

And expand.json

 {"children": [ { "name": "x", "id": "x", "size": 1983 }, { "name": "y", "id": "y", "size": 2047 }, { "name": "z", "id": "z", "size": 1375 }, { "name": "t", "id": "t", "size": 1375 } ]} 

PS: I had to sort the array of nodes, otherwise bad things happened to the chart, I can’t understand why.

+1
source share
3 answers

Here's the fiddle for a working solution. I think the problem was how you declare your id and sort them based on the index of the array. You must allow the identifier to be declared using a smoothing code, and then sort them by the specified identifier. Also in your recursive function, you can declare the parent first, and then the children later.

 function recurse(node) { if(!node.id) node.id = ++i; nodes.push(node); if (node.children) node.children.forEach(recurse); } 
+2
source

I managed to find a solution for this, starting with Yogesh's answer. Here is the code to add to the update () function.

  var currentNodes = force.nodes(); var nodes = flatten(root); var actualNodes = []; var values = currentNodes.map(function(obj) { return obj.name}); var newNodesValues = nodes.map(function(obj) { return obj.name }); for(var i = 0; i < currentNodes.length; i++) { if(newNodesValues.indexOf(currentNodes[i].name) !== -1) { actualNodes.push(currentNodes[i]); } } for(var i = 0; i < nodes.length; i++) { if(values.indexOf(nodes[i].name) == -1) { actualNodes.push(nodes[i]); } } nodes = actualNodes; var links = d3.layout.tree().links(nodes); // Restart the force layout. force .nodes(nodes) .links(links) .linkDistance(55) .start(); 
0
source

The following should do the trick:

 var i = 0; 

...

 var link = vis.selectAll(".link") .data(links, function (d) { return d.id || (d.id = ++i); }); 

...

 var node = vis.selectAll("g.node") .data(nodes, function (d) { return d.id || (d.id = ++i); }); 

The second argument to the data () function is a callback function that, when called with a zero point, returns a key that connects each DOM node to the corresponding database. When you do not provide such a function, d3 is left without an option, but to use an index to bind datum to DOM nodes.

0
source

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


All Articles