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 schoolfunction
notation to accessthis
) to isolate the DOM nodes for the groups (exposed by D3 asthis
), then useselect
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.