Question

I'm trying to create a d3 force graph, where nodes are added (and later removed) dynamically. This example does roughly what I want, but I am having trouble adapting it, as the comments are rather sparse.

My main questions are around the start() function:

function start() {
  link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
  link.enter().insert("line", ".node").attr("class", "link");
  link.exit().remove();

  node = node.data(force.nodes(), function(d) { return d.id;});
  node.enter().append("circle").attr("class", function(d) { return "node " + d.id; }).attr("r", 8);
  node.exit().remove();

  force.start();
}

Firstly, what exactly is link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; }); doing? The examples that just make a static layout instead use this style:

link = svg.selectAll("line")
    .data(links)
    .enter()
    .append("line")
    .attr("class", "link");

Is that code doing the same as the first two lines in start()? (Lines 4..6 seem to be following this style a bit more closely so, again, why are they using a different approach (insert vs. append)?)

And, why does line 2, refer to .node, when it is dealing with links, not nodes?

My last question is, is it safe to keep calling force.start() like that? For the sake of example, if start() got called 100 times, 10ms apart, would that matter? Would it cause any extra calls to the tick function? (I'm just wondering if I should be calling force.stop() at the top of start() )? (API docs for d3.layout.force don't explicitly say.)


Bonus Question: Are these lines actually doing anything?

var node = svg.selectAll(".node"),
  link = svg.selectAll(".link");

As there are no nodes at this point, are they equivalent to:

var node,link;
Was it helpful?

Solution

Regarding your first question, this code is updating the data bound to the DOM elements that represent the links. That is, we take the existing selection link (which contains all the links so far) and call .data() on it. The second argument to .data() tells D3 how to determine whether a DOM element represents a data element -- in this case they are linked by the source and target ID.

This new selection (old selection + new data bound) is then assigned back to link, so that the variable contains again the selection of the link elements, but now with the new data. Then we can add new elements for the .enter() selection, remove elements from the .exit() selection and update the rest.

The code from the static example you've posted explicitly selects all the elements again (svg.selectAll("line")) instead of using a selection set earlier. The other difference is that it operates only on the .enter() selection. This is fine for the static version as you're only adding the links once and not modifying them, but in your case you need to handle the other selections as well.

The code to add new links uses .insert("line", ".node") and not .append() simply to preserve the relative order of the elements. SVG elements are rendered in the order they are defined in, so a link added after a node would appear in front of the node. By using .insert("line", ".node"), you're telling D3 to insert a new line element just before the first .node element. That is, add any new links before the first node in the DOM so that no node is obscured by a line.

In general, it is safe to call force.start() as many times and as frequently as you like. All it does in this context is reset the cooling parameter that causes the layout to slow down and eventually settle. Unless the layout has cooled down all the way, it should not cause any extra calls to the tick() function (which wouldn't be a problem though).

The lines you mention at the end have the effect that they establish the variables as a (potentially empty) selection. This means you can use them with the D3 methods such as .data() to bind new data to them (and this is in fact used in the example). Just declaring the variables does not do the same thing because it doesn't assign D3 selections. The fact that the selection is empty does not matter in this context.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top