Selections in D3

If you have used D3 for a while, you are definitely used to the sequence of select/selectAll->data->enter->append. Most of the time, we just copy that piece of code from the online examples. In this post, I will try to summarize my understanding of D3 selections. I believe knowledge of how selections work behind the things can enable us to create more interactive charts in D3.

select

To add elements onto the web page, we first need to select the element on to which the new elements will be attached. For example, if I want to add a header tag with the content Hello, World to a div element, I first need to select that div and then add the header like this:

d3.select("div")
  .append("h1")
  .html("Hello, World");

An important thing to note here is, if there are multiple div elements on the web page, d3 will select the first div that it encounters.

selectAll

If we want to select all the instances of an element, we can use selectAll. The following code will append the text to all the div elements on the web page.

d3.selectAll("div")
  .append("h1")
  .html("Hello, World");

data binding

We can add data to the selected elements in D3, by using data method on the selected d3 element objects. This can be achieved as follows:

 var colors = ["red", "green", "blue"];
 d3.selectAll("div").data(colors);
 d3.selectAll("div").style("color", (d) => d);

Above, the collection colors is set on all the selected div elements. In the arrow function above, d takes value of each of the item in the collection. In the body of the function, that element itself is returned; so that the color value is different for each of the selected divs.

In the above example, I am assuming the number of elements selected, and the number of items in the collection are same.

mismatch between #(elements) and data

When number of selected elements and the number of data items is different, one of the following scenarios take place:

  1. If number of data items is greater than the selected elements, then new elements will be created and added. eg.consider the following code:

     var text = ["First Para", "Second Para", "Third Para"];
    
     d3.select("#chart").selectAll("p")
       .data(text)
       .enter()
       .append("p")
       .html(d=>d);
    

    If there are no p elements when enter() is called(or less than 3), enter() will prepare the remaining number of placeholders for the elements. Elements are not created yet. Then append(p) adds a p for each unmatched data item. We get the following output.

     First Para
     Second Para
     Third Para
    
  2. If number of data items is less than the selected elements, then D3 ignores the rest of the selected elements than the data items. For example, if we add this code to the existing code,

     var nums2 = [6];
     d3.selectAll("p")
       .data(nums2)
       .html(d => d);
    

    D3 will only update the first paragraph(as there is only one data item) and leave the rest of the ps as it is. So the output will look something like this:

     1
     Second Para
     Third Para
    
  3. If number of data items is more than the selected elements, and we don’t want to create new elements then we can skip the enter() and D3 will just update the first few selected elements which are mapped to the data items. For example, if our html has 5 p elements, and we run following code,

     var nums = [1,2,3];
     d3.selectAll("p").data(nums).text(d => d);
    

    D3 will only update the first 3 ps and won’t change the remaining 2 pelements