Export Image
Export Code

renenadorp

Last edited Aug 21, 2022
Created on Aug 21, 2022

A response to a question in the D3 Slack about different numbers of nested rectangles.

Original question by May Tan:

What i'm trying to do is add varying number of rectangles based on "num" in the data. The size of rectangle depends on the type of fruit. I realise that the rect size did update when the apple is changed into a lemon. However rect size didn't update on the last update when apple was eaten. My question is how do I write the selections when the data join is not based on number of array items, but range of some data. I am also sure i'm writing the update part wrongly as the size of rect did not get updated. Thanks for your help!

Answer:

Ahh I see what you mean. You want to essentially "break out" into a new update pattern, within each <g> element, so that each has its own unique data driven collection of rectangles. Here's a solution for it:

const groups = groupsEnter.merge(groupsUpdate);

groups.each(function (d) {
  const innerGroup = select(this);
  const rects = innerGroup
    .selectAll('rect')
    .data((d) => d3.range(d.num).map(() => d));

  rects
    .enter()
    .append('rect')
    .attr('height', '30px')
    .attr('stroke', 'black')
    .merge(rects)
    .attr('y', (d, i) => -40 * (i + 1))
    .attr('width', (d) => radiusScale(d.type));

  rects.exit().remove();
});

The trick is to use selection.each (and the old school function notation to access this) to isolate the DOM nodes for the groups (exposed by D3 as this), then use select to create a new D3 selection for each individual <g> element. With this in hand, we can have another nested but totally independent General Update Pattern, allowing us to make a different number of rectangles for each group.

MIT Licensed