d3.js update pattern Merging selections


Example

The update pattern in D3 version 3

A correct understanding of how the “enter”, “update” and “exit” selections work is fundamental for properly changing the dataviz using D3.

Since D3 version 3 (actually, since version 2), this snippet could set the transitions for both “enter” and “update” selections (live demo here):

var divs = body.selectAll("div")
    .data(data);//binding the data

divs.enter()//enter selection
    .append("div")
    .style("width", "0px");

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

Giving this result after the transition:

enter image description here

But what happen with the exactly same code if we use D3 version 4? You can see it in this live demo: nothing!

Why?

Changes in the update pattern in D3 version 4

Let’s check the code. First, we have divs. This selection binds the data to the <div>.

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

Then, we have divs.enter(), which is a selection that contains all the data with unmatched elements. This selection contains all the divs in the first time we call the function draw, and we set their widths to zero.

divs.enter()
    .append("div")
    .style("width", "0px");

Then we have divs.transition(), and here we have the interesting behaviour: In D3 version 3, divs.transition() makes all the <div> in the “enter” selection changing to their final width. But that makes no sense! divs doesn’t contain the “enter” selection, and should not modify any DOM element.

There is a reason why this strange behaviour have been introduced in D3 version 2 and 3 (source here), and it was “corrected” in D3 version 4. Thus, in the live demo above, nothing happens, and this is expected! Furthermore, if you click the button, all the previous six bars appear, because they are now in the “update” selection, not anymore in the “enter” selection.

For the transition acting over the “enter” selection, we have to create separate variables or, an easier approach, merging the selections:

divs.enter()//enter selection
    .append("div")
    .style("width", "0px")
    .merge(divs)//from now on, enter + update selections
    .transition().duration(1000).style("width", function(d) { return d + "px"; })
    .attr("class", "divchart")
    .text(function(d) { return d; });

Now, we merged the “enter” and the “update” selections. See how it works in this live demo.