Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Basic d3: why can you select things that don't exist yet?

I've been learning about d3, and I'm a bit confused about selecting. Consider the following example:

http://bl.ocks.org/mbostock/1021841

Specifically, let's look at this line:

var node = svg.selectAll(".node")
    .data(nodes)
  .enter().append("circle")
    .attr("class", "node")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 8)
    .style("fill", function(d, i) { return fill(i & 3); })
    .style("stroke", function(d, i) { return d3.rgb(fill(i & 3)).darker(2); })
    .call(force.drag)
    .on("mousedown", function() { d3.event.stopPropagation(); });

In the documentation it says, "A selection is an array of elements pulled from the current document." I interpret this to mean that svg.selectAll(.node) creates an array of elements of class .node pulled from the current document, but as far as I can tell there are no such elements! Unless I'm confused - and I'm almost certain that I am - the only place in the document where something is given the class "node" is after the selection has already occurred (when we write .attr("class", "node")).

So what is going on here? What does svg.selectAll(".node") actually select?

like image 956
Paul Siegel Avatar asked Oct 28 '15 19:10

Paul Siegel


1 Answers

Although, at first sight, this may look like a simple and silly question, the answer to it is probably the most important one for everyone trying to do some serious work with D3.js. Always keep in mind, that D3.js is all about binding data to some DOM structure and providing the means of keeping your data and the document in sync.

Your statement does exactly that:

  1. Select all elements having class node. This may very well return an empty selection, as it is in your case, but it will still be a d3.selection.

  2. Bind data to this selection. Based on the above mentioned selection this will, on a per-element basis, compute a join checking if the new data is a) not yet bound to this selection, b) has been bound before, or c) was bound before but is not included in the new data any more. Depending on the result of this check the selection will be divided into an enter, an update, or an exit selection, respectively.

  3. Because your selection was empty in the first place. All data will end up in the enter selection which is retrieved by calling selection.enter().

  4. You are now able to append your new elements corresponding to the newly bound data by calling selection.append() on the enter selection.

Have a look at the excellent article Thinking with Joins by Mike Bostock for a more in-depth explanation of what is going on.

like image 69
altocumulus Avatar answered Nov 02 '22 18:11

altocumulus