Codementor Events

D3.js merge() extended - small multiples

Published Mar 10, 2020Last updated Sep 05, 2020
D3.js merge() extended - small multiples

According to Wikipedia a Small Multiple is:

a series of similar graphs or charts using the same scale and axes, allowing them to be easily compared

They can be really powerful and there are some wonderful best practice examples out there including:

I thought it would be helpful to follow up on my D3.js merge() post for small multiples.

Here's the same ESSENTIAL code with one addition - can you spot it?

      //step 1 - JOIN (with or without a key)
      var my_group = svg.selectAll('.**chart_group**')
                        .data(data, d => d.id);
                        
      //step 2 - EXIT and REMOVE
      my_group.exit().remove();
      
      //step 3 - ENTER
      var enter = my_group.enter().append("g").attr("class","**chart_group**");

      //step 4 - APPEND
      enter.append("rect").attr("class","group_rect");
      enter.append("text").attr("class","group_text");
      enter.append("image").attr("class","group_image");
      enter.append("g").attr("class","sub_chart_group");
      
      //step 5 - MERGE
      my_group = my_group.merge(enter);

So each 'group' has a a bounding rectangle, title and image BUT it also has data so needs a 4th element appended - sub_chart_group.

SO after you have added the properties to the elements in my_group - as seen in the position_group_elements function in my simple demo.

You also need to think about position. Depending on your UI needs, you will need to position each group in the right place on the svg.

In my simple demo, I've chosen a simple approach with a panels_per_row constant.

I use this to set x_pos and y_pos in the position_group_elements function and then I add a translate attribute to place each panel in the correct position.

my_group.select(".sub_chart_group")
    .attr("transform",d => "translate(" + d.x_pos + "," + d.y_pos + ")");

Now for the exciting bit - creating a sub_group and appending the data.

     //step 1 - JOIN (with or without a key)
       var sub_group = my_group.select(".sub_chart_group")
       					 .selectAll('.sub_chart_elements_group')
                         .data(d => [d.chart_data]);
                         
       //step 2 - EXIT and REMOVE
       sub_group.exit().remove();
       
       //step 3 - ENTER
       var sub_enter = sub_group.enter().append("g")
       .attr("class","sub_chart_elements_group");

       //step 4 - APPEND
       sub_enter.append("path").attr("class","chart_line");
       sub_enter.append("path").attr("class","chart_area");
       
       //step 5 - MERGE
       sub_group = sub_group.merge(sub_enter);

The code is very similar right?

BUT there are two key differences:

  • instead of the svg I've used ** my_group.select(".svg_chart_group")** as the selector
  • I've used a new name for the sub_enter groups "sub_chart_elements_group"

The final step, is to select the elements in sub_group and add the properties. For example:

 sub_group.select(".chart_area")
          .attr("d", area)
          .attr("stroke","none")
          .attr("fill",d => d.color)
          .attr("fill-opacity",0.2)
          .attr("transform","translate(" + start_x + "," + start_y + ")");

That's it.

Have a look at my simple demo to see all of this in one place.

Once you've mastered the data appending you can go on to create your own beautiful small multiple charts.

If you want to see the same process using .join() instead of .merge() have a look at this simple demo.

Discover and read more posts from Bryony Miles
get started