DEV Community

Cover image for D3.js Join Semantics - A Conceptual Look
Sahan
Sahan

Posted on • Originally published at sahansera.dev on

D3.js Join Semantics - A Conceptual Look

It’s really easy to get started with D3 when you go through their excellent documentation. However, the goal of this post is to give you a sense of what patterns it uses underneath for joining DOM elements and data items.

The key element of D3 is that it treats data you want to visualise as a database. We can also think that D3 treats the elements you have on your webpage as a database.

Data Join Semantics

Let’s say we have the following SVG elements in our DOM and a variable data with some values we want to bind it with.

<svg x="100" y="100">
    <rect></rect>
    <rect></rect>
    <rect></rect>
    <rect></rect>
    <rect></rect>
</svg>
Enter fullscreen mode Exit fullscreen mode

Now let’s see how D3 joins the data with DOM elements. We will use the above structure as a reference.

Update

d3.selectAll('rect').data([1, 2, 3, 4, 5])
Enter fullscreen mode Exit fullscreen mode

So we have a database of DOM elements and a database of data items. When we have a 1:1 mapping with them we call it the Update section. Once this association is formed internally in D3, we can manipulate our DOM elements using methods such as .attr().

This situation is called “Update” stage. Think of this as a natural join between two database tables.

Enter

What if we have more data elements than our DOM elements?

For such cases that go into a different area in the selection called Enter area. That’s when we can access that area with the .enter()method in D3. We can then tell D3 to what to do with those extra data items. For instance, if we want to create new ‘rect’ DOM elements, we can do a,

d3.selectAll('rect')
    .data([1, 2, 3, 4, 5, **6**])
    .enter() // accessing the Enter area
    .append('rect') // telling what to do with it
    .attr('height', ...) // rest of the manipulations
Enter fullscreen mode Exit fullscreen mode

Exit

Finally, we have the case of having more DOM elements than our data elements.

Imagine now we have 5 data elements but only 4 DOM elements. So these extra UI elements go into Exit area. Just like before, we just need to tell D3 what to do with those extra items? Remove you say? 👍

d3.selectAll('rect')
    .data([1, 2, 3, 4, 5])
    .exit()
    .remove();
Enter fullscreen mode Exit fullscreen mode

Mapping with Relational Algebra

Remember we are now thinking about D3 in terms of database. Let’s see how we can map this into relational algebra.

d3-exit-update-enter.png

As we can see, when we have UI elements and data with 1:1 mapping, it like a natural join. When we have more data than UI elements, it like an anti-join where we get the extra data elements in Enter stage. As with the Exit stage, we get the extra UI elements that do not map with data.

Now we know the underpinning principles of D3 join semantics. This helps us to dive into a D3.js code without worrying too much about syntax.

What would happen if we do a .selectAll() on an empty set of DOM elements? Can we still use .enter()? There’s a well-known pattern for that as well 😊I will explain that in a separate blog post.

References

  1. https://bost.ocks.org/mike/join/
  2. https://en.m.wikipedia.org/wiki/Relational_algebra

Top comments (0)