D3: .enter, .exit, and .merge. Data joining with D3.
By: Leon T Johnson
Tags: Data Visualization, SVG, D3, Data Science, D3js

D3: .enter, .exit, and .merge

Thinking with Joins

Before you do anything, check out this post by Mike Bostock, the guy behind D3.

I’ve just been racking my brain for the last week trying to understand what in the world .enter , .exit , and .merge mean in D3. I mean, they’re arguably the most important aspects to D3 development (they’re the engines behind data joining, which is the engine behind D3, really), but their names are just so confusing! “Enter”? “Exit”? Why not just “add” and “take away”? I feel it’s important for me to take some time and try to explain what’s going on in my own words, if anything to prove to myself that I get what is happening.

D3 Joining

Suppose we have a bunch of data , (x, y) points, that we want to plot in a scatter plot of circle s. Check out this code adopted from Bostock’s post, linked above:

var svg = d3.select("body")

.append("svg")

.attr("width", w)

.attr("height", h); var circle = svg.selectAll("circle")

.data(data);



circle.exit().remove();



circle.enter().append("circle")

.attr("r", 2.5)

.merge(circle)

.attr("cx", function(d) { return d.x; })

.attr("cy", function(d) { return d.y; });

This syntax … Mike … Mike … whys?

A New Way of Thinking

Before we go any further, I need to make some important points (as Mike does in his post)

D3 does not have native ability to create or add multiple DOM elements svg.selectAll("circle").data(data) creates a connection, not an object

Okay, at first glance it just doesn’t make any sense. But, once you realize the thing I’m about to say, I think everything falls into place a bit better:

First, D3 defines the relationship between individual data points and individual visual elements. Then, it maintains that relationship for all data and all visual elements.

And finally, the most important point I think

To D3, a data point cannot exist without its visual element counterpart. They are essentially married, bound eternally.

That said, from here on in, I will refer to data and visual elements together as sets of individual pairs or atomic relationships. I’ll call each individual data point-visual element pair “a data binding” or “a binding” for short.

what D3 sees

This is the overall picture of what D3 is looking at any time a script is called, and it’s a good way to think about the naming convention for these joining methods. So, if there are bindings with new data that need to enter the SVG we use .enter , or existing bindings that need to be updated with new entered data in the SVG we use .merge . We can update data simply by updating the attr attributes. And, if there are bindings with surplus visual elements that need exit the SVG we use .exit . These are the only three possible states for a binding.

.enter : In the data, not on the screen

: In the data, not on the screen .merge : Changed in the data, Not Changed on the screen

: in the data, on the screen Updating attr s : Changed in the data, Not Changed on the screen

s : in the data, on the screen .exit : Not in the data, is on the screen

In another way, we can think about an individual binding like this:

It took me far too long to make this …

Now with this in mind, let’s go through each part of the above code, step by step with our new interpretation of what is going on:

var svg = d3.select("body")

.append("svg")

.attr("width", w)

.attr("height", h);

Before we can do anything, we need an SVG element in which to place our visual elements (which are each bound to some data point). We give this SVG some attributes like width and height of the visualization outline. svg , here, is our connection or reference to the SVG element in the DOM. Everything else that follows further defines that connection as it pertains to the relationship between our data and our visual elements. Let’s continue through the code.

var circle = svg.selectAll("circle")

.data(data);

Read it as “For any individual circle to exist” (hence the .selectAll ) in our svg , define the binding between each circle and each individual data point (in data ) as follows …

circle.exit().remove();

In the exit state (the corresponding visual element no longer exists), simply remove the binding from our SVG. Note: Here, we could have had a transition, or we could have had something happen with another binding in the SVG, but we chose to stick with just one particular action-to-do in the exit state, namely remove .

circle.enter().append("circle")

.attr("r", 2.5)

In the enter state (the binding’s corresponding data point is new, not represented by any visual element in the SVG), append the corresponding circle with radius 2.5px (i.e., the "r" attribute or attr ) into the SVG. Note: Think about it here that we’ve already created the binding between the new data point and its circle, we just haven’t defined exactly what to do with that (abstract) circle when it’s new to the SVG until enter .

.merge(circle)

.attr("cx", function(d) { return d.x; })

.attr("cy", function(d) { return d.y; });

Finally, if there is already a visual element represented for that binding’s data point in our SVG (we’ve seen that data and that particular circle before), you may need to make changes to its visual elements because of changes in the other elements (e.g., less bars in a bar plot). You can use merge in this case to make any necessary updates (e.g., above, the center of the circle , ( cx , cy ) should be defined by the data point’s x and y attributes, d => d.x and d => d.y ). Note: We’re using function notation, as we do always in D3, because we’re defining a relationship, not an actual scalar value.

Then boom! We have a visualization that makes people tear up with joy!

Object Constancy

As a final note, it is a good idea to maintain object constancy in all of this. Just using the .data(dataset) syntax doesn’t guarantee that data points are bound exactly to their corresponding visual elements (though, in some simple cases, it’s enough for the viz to work fine). In fact, it only binds the data and the visual elements by the order in which they occur; but things can change, which means the order(s) can change, independently. So, we use keys to keep things bound correctly. In this way, you’d typically write something like

var bar = svg.selectAll("rect")

.data(dataset, d => d.id); // Unique IDs for the visual