D3js force layout destroy and reset
Solution 1
- No. You have to do this manually.
- You could have a look at the DOM, but it looks like you're deleting everything.
-
I'm guessing that this happens because you're not actually deleting the nodes/links from the force layout. At some point, you've given the variables
nodes
andlinks
to the force layout. Changing what those names point to (i.e.[]
) doesn't change the reference in the force layout. That is, the data objects are still there and referenced. There are two ways to remove them. You can either modify thenodes
andlinks
in place (e.g. with.slice()
), or reset them explicitly in the force layout.nodes = []; links = []; force.nodes(nodes); force.links(links);
Hard to say without a specific example, but the answer is most likely no. Javascript is garbage collected, so doing it manually shouldn't have an impact.
Solution 2
I did it with:
nodeCircles = {};
node.remove();
link.remove();
svg.clear();
nodes = [];
links = [];
Just put this into a method and then recreate your force and svg. That works great.
Related videos on Youtube
Jason
Updated on June 04, 2022Comments
-
Jason about 2 years
Based on two D3 examples: Force layout (http://bl.ocks.org/mbostock/1095795) and clustered force layout (http://bl.ocks.org/mbostock/1748247), I managed to built a force layout with a few independent point of gravity to control nodes position on top of the links between nodes.
// Set up map function map_init(){ force = d3.layout.force() .nodes(nodes) .links(links) .size([width, height]) .on("tick", tick); svg = d3.select("#map").append("svg") .attr("width", width) .attr("height", height); link = $map.selectAll(".link"); node = $map.selectAll(".node"); d3.json("graph.json", function(error, graph) { // set up nodes for( i = 0; i < graph.nodes.length; i++ ){ nodes.push( graph.nodes[i] ); } // position nodes to three different gravity centres based on theme for( i = 0; i < nodes.length; i++ ){ if ( nodes[i].theme == "theme1" ){ nodes[i].cx = 100; nodes[i].cy = 100; } else if ( nodes[i].theme == "theme2" ){ nodes[i].cx = 300; nodes[i].cy = 300; } else if ( nodes[i].theme == "theme3" ){ nodes[i].cx = 500; nodes[i].cy = 500; } } // link nodes of the same theme theme1_nodes = nodes.filter(function(d){ return (d.theme == "theme1"); }); theme2_nodes = nodes.filter(function(d){ return (d.theme == "theme2"); }); theme3_nodes = nodes.filter(function(d){ return (d.theme == "theme3"); }); for (i = 0; i < theme1_nodes.length-1; i++){ links.push({ source: theme1_nodes[i], target: theme1_nodes[i+1] }); } for (i = 0; i < theme2_nodes.length-1; i++){ links.push({ source: theme2_nodes[i], target: theme2_nodes[i+1] }); } for (i = 0; i < theme3_nodes.length-1; i++){ links.push({ source: theme3_nodes[i], target: theme3_nodes[i+1] }); } start(); }); } // Start function start() { link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; }); link.enter() .insert("svg:line") .attr("class", "link"); link.exit() .remove(); node = node.data(force.nodes(), function(d) { return d.id; }); var nodeEnter = node.enter() .append("svg:g") .attr("class", "node"); .on("click", map_nodeClick); node.exit().remove(); // Enter node information nodeEnter.each(function(d) { theTitle = d3.select(this).append("svg:text") .attr("font-family", "Helvetica") .attr("class", "title") .text( d.title ); }); // More content to go into each node // . // . // . force.start(); } // Tick function tick(e) { node .each(gravity(.2 * e.alpha)) .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); 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; }); } // Gravity function gravity(alpha) { return function(d) { d.y += (d.cy - d.y) * alpha; d.x += (d.cx - d.x) * alpha; }; } // Set up when page first loads map_init();
In order to reset/restart the force layout anytime without reloading the page, I bound the following function to a reset button:
// Remove force layout and data function map_remove(){ node.remove(); link.remove(); svg.remove(); nodes = []; links = []; } // Reset button $('a#reset').click(function(e){ e.preventDefault(); map_remove(); map_init(); });
This webpage is displayed on a device accessible by group of people. Only loaded once in the morning and stayed running on iPad Safari for 12 hours. Link between nodes ideally changes dynamically based on users input (to be implemented). Apart from the force layout there are other info on the webpage. An option to relaunch/reset the force layout without reloading the page is required.
- Is there a built-in method to destroy the D3 force layout and its data?
- Currently this works ok as no extra DOM elements were created and no errors found from inspector. However I am not sure how to check if all the D3 objects has been cleared/emptied so no duplicated data was stored/accumulated?
- Currently each reset for some reasons pull the nodes closer and closer to the centre of the map. Have I missed out something in the map_remove() function?
- Would a complete restart of the D3 force layout improve the performance of the browser at any point? i.e. clearing up the memory for painting the SVG?
-
Carr over 7 yearsI'm sorry that I was little bit confused about "...in place (e.g. with .slice())...". is that should be
splice()
? -
Lars Kotthoff over 7 yearsYou can use both, depending on what exactly you want to do.