d3.js Selections The role of placeholders in "enter" selections


Example

What is an enter selection?

In D3.js, when one binds data to DOM elements, three situations are possible:

  1. The number of elements and the number of data points are the same;
  2. There are more elements than data points;
  3. There are more data points than elements;

In the situation #3, all the data points without a corresponding DOM element belong to the enter selection. Thus, In D3.js, enter selections are selections that, after joining elements to the data, contains all the data that don't match any DOM element. If we use an append function in an enter selection, D3 will create new elements binding that data for us.

This is a Venn diagram explaining the possible situations regarding number of data points/number of DOM elements:

enter image description here

As we can see, the enter selection is the blue area at the left: data points without corresponding DOM elements.

The structure of the enter selection

Typically, an enter selection has these 4 steps:

  1. selectAll: Select elements in the DOM;
  2. data: Counts and parses the data;
  3. enter: Comparing the selection with the data, creates new elements;
  4. append: Append the actual elements in the DOM;

This is a very basic example (look at the 4 steps in the var divs):

var data = [40, 80, 150, 160, 230, 260];

var body = d3.select("body");

var divs = body.selectAll("div")
    .data(data)
    .enter()
    .append("div");

divs.style("width", function(d) { return d + "px"; })
    .attr("class", "divchart")
    .text(function(d) { return d; });

And this is the result (jsfiddle here):

enter image description here

Notice that, in this case, we used selectAll("div") as the first line in our "enter" selection variable. We have a dataset with 6 values, and D3 created 6 divs for us.

The role of placeholders

But suppose that we already have a div in our document, something like <div>This is my chart</div> at the top. In that case, when we write:

body.selectAll("div")

we are selecting that existent div. So, our enter selection will have only 5 datum without matching elements. For instance, in this jsfiddle, where there is already a div in the HTML ("This is my chart"), this will be the outcome:

enter image description here

We don't see the value "40" anymore: our first "bar" disappeared, and the reason for that is that our "enter" selection now has only 5 elements.

What we have to understand here is that in the first line of our enter selection variable, selectAll("div"), those divs are just placeholders. We don't have to select all the divs if we are appending divs, or all the circle if we are appending circle. We can select different things. And, if we don't plan to have an "update" or an "exit" selection, we can select anything:

var divs = body.selectAll(".foo")//this class doesn't exist, and never will!
    .data(data)
    .enter()
    .append("div");

Doing this way, we are selecting all the ".foo". Here, "foo" is a class that not only doesn't exist, but also it's never created anywhere else in the code! But it doesn't matter, this is only a placeholder. The logic is this:

If in your "enter" selection you select something that doesn't exist, your "enter" selection will always contain all your data.

Now, selecting .foo, our "enter" selection have 6 elements, even if we already have a div in the document:

enter image description here

And here is the corresponding jsfiddle.

Selecting null

By far, the best way to guarantee that you are selecting nothing is selecting null. Not only that, but this alternative is way faster than any other.

Thus, for an enter selection, just do:

selection.selectAll(null)
    .data(data)
    .enter()
    .append(element);

Here is a demo fiddle: https://jsfiddle.net/gerardofurtado/th6s160p/

Conclusion

When dealing with "enter" selections, take extra care to do not select something that already exists. You can use anything in your selectAll, even things that don't exist and will never exist (if you don't plan to have an "update" or an "exit" selection).

The code in the examples is based on this code by Mike Bostock: https://bl.ocks.org/mbostock/7322386