Simple D3.js version 5 data binding and updating example and code

This post goes through the process of binding data to elements and creating a simple updatable SVG graphic using D3.js version 5.

Data binding and updating in D3.js version 5

In D3.js version 4 there was quite a big update to how data is bound to elements and updated. This means there is a lot of code which currently will subtly fail to update visualisations created.

Today I am using the current latest version of D3.js (version 5) to show a simple example binding and updating data. This will demonstrate the new system of having an updatable visualisation based on the data.

Creating the SVG and page buttons

The body of the page will only contain a SVG to draw onto and two buttons. These buttons will let the user increment the number of times that the button has been pressed. Below is the code to add these three elements to the page.

<p>
    <button onclick="buttonPress(1)">Button 1</button>
    <button onclick="buttonPress(2)">Button 2</button>
</p>
<p>
    <svg id="mainSvg" height="100" width="300"></svg>
</p>

The onclick functions on the buttons will be explained in the next step. These functions are called whenever the button is pressed.

I have given the SVG an ID so that it may be referred to, and a width and height appropriate to the content we are going to add. In more advanced graphs you may resize the SVG depending on the content.

Creating the data structure and update buttons

To illustrate data binding I am going to create a simple data structure to hold information about the number of times a button has been pressed. Below is the data structure that will hold this data.

var data = [
    {"title": "Button One", "timesPressed": 0},
    {"title": "Button Two", "timesPressed": 0}
];

Each time the button is pressed I will add one to the count.  The following function will be triggered when the buttons are pressed to increment the count.

function buttonPress(buttonNumber) {
    data[buttonNumber - 1].timesPressed++;
    update();
}

The variable buttonNumber has one taken off it since arrays are 0 indexed. Once the timesPressed key is incremented the update() function is called. This will be our primary function to create and update the visualisation.

Selecting the SVG

First we need to select the SVG we are going to be drawing, this can be done by the following code.

var s = d3.select("#mainSvg");

This saves the SVG reference to the s variable to access it.

Entering, Updating and Exiting

When creating and updating the SVG there are three major ways to operate on a selection.

  • Update – Is used for all elements which already have data bound to it
  • Enter – Is used for when the data you are binding has no element already bound to it
  • Exit – Is used when there was previously data bound but there is no longer

D3 splits up divides all operations on elements to these three groups and allow you to customize how each is dealt with.

Here I will show and explain what I will be doing in each part and then show the full context of the update function.

Update – Selecting all elements that are currently bound to data

The first part of generating SVG’s from data is selecting the elements currently on the page bound to data. Each element generated by D3 is bound to a specific piece of data.

var titleTextSelection = s.selectAll(".buttonTitles").data(data);

Here I select every element under the main SVG element with the class buttonTitles. Using data() I pass in my current data set which selects elements currently bound to data.

Any operations on this selection will be performed on these elements already on the page, whose data elements are still in the values passed to the data() call.

This selection is saved into a variable as this selection also allows us to operate on elements that need to be created (added into the data set), and those that have been removed from it.

At the moment I am not going to do anything currently to the elements existing on the page so I don’t use this selection currently.

Exit – Handling elements no longer bound to data

The next step is to handle elements that are no longer bound to the data. These would have been elements created by the enter() call previously but their data no longer exists in the value passed into data().

titleTextSelection.exit().remove();

Above I use the selection created before and call the .exit() method on it. Anything chained after the .exit() method will operate on the elements that no longer part of the data. Since I no longer want these elements to appear on the SVG I call remove() to remove them from the SVG.

Enter – Creating new elements from data currently not bound

On the first call to our update function, the first two function calls will do nothing as there is no elements currently bound to data. This means there is nothing to select and nothing to remove. However .enter() will create the appropriate elements the first time around.

var titleTextEnter = titleTextSelection
    .enter()
    .append("text")
    .attr("class", "buttonTitles")
    .attr("y",  function(d, index) { return 20 + (index * 50); })
;

Here the selection is used again and this time enter() is called on it to operate on all data elements that are new. I first append a text element using append("text") and set its class and position on the page.

Each chained function can either take a specific value or a function. The first parameter to this function is the data representing the element and the second is the index. Above I am using the index to offset each piece of text in the y dimension.

It is important to note that these function calls will only be made when elements are added to the page. If you want data to be updated you will want to add them to the update function calls.

I save this to a variable which holds all the elements that have been created by the enter() function.

Enter and Update – Combining operations that need to run on both new and old elements

The final step in my update function is to set the value of the text element. This could be done by writing the same function in both the update and enter selections however there is an easier way in D3 version 5. We are able to use the merge() function to merge two selections and operate on both of them.

titleTextSelection.merge(titleTextEnter)
    .text(function(d) {
        return d.title + " has been pressed " + d.timesPressed + " times";
    });

Here I use the selection of all elements on the page and merge in the selection of all created elements. This lets me apply any functions to both new and old elements reducing code duplication.

In this case I set the text of the element to be the title and the number of times the button has been pressed.

Combining the update parts together

Now I can combine the above code to present my single update function.

function update() {
    var titleTextSelection = s.selectAll(".buttonTitles")
        .data(data);

    titleTextSelection.exit().remove();

    var titleTextEnter = titleTextSelection
        .enter()
        .append("text")
        .attr("class", "buttonTitles")
        .attr("y",  function(d, index) { return 20 + (index * 50); })
    ;

    titleTextSelection.merge(titleTextEnter)
        .text(function(d) {
            return d.title + " has been pressed " + d.timesPressed + " times";
        });
}

The full code above has the four major parts identified above:

  1. Creates the new selection with the new data using data()
  2. Removes old elements no longer in the data using exit()
  3. Creates new elements in the data using enter()
  4. Operates on both new and current elements using merge()

Using these four functions you can create complex data visualisations focussing on the data rather than how you will display it.

Summary

Here I have explained the four important functions that can be used for data visualisations. Using a simple data structure the principle of creating, updating and removing elements as the data changes is demonstrated. A simple SVG and page is created to show how the functions work.

In the future tutorials I will explain how you can use a more complex data structure by chaining enter, update and remove calls.

The full code for this is available on my website which includes a live example.

If you have any questions feel free to comment on the following blog post.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.