Nested Joins

by IPWright83

HTML

<svg width="800" height="800">
    <g class="container" transform="translate(400, 400)"></g>
</svg>

JavaScript

const RADIUS = 50;
const data = [
    ["circle1", "circle2"],
    ["circle3", "circle4"],
    ["circle5", "circle6"],
    ["circle7", "circle8", "circle9"]
];

render = (data) => {
    const join = d3
        .select(".container")
        .selectAll("g")
        .data(data);

    // Remove old groups
    join
    	.exit()
        .transition()
        .duration(500)
        .attr("transform", "scale(0)")
        .remove();

    // Create the new groups
    const groups = join
    	.enter()
        .append("g")
        .attr("class", "outer");
    
    // Add in the new circles
    groups.append("circle")
    	.attr("r", 0)
        .style("fill", "steelblue")
        .transition()
        .duration(500)
        .attr("r", RADIUS);

    // Merge the new groups with the existing groups
    // and apply an appropriate translation
    const innerJoin = groups
    	.merge(join)
        .attr("transform", d => `translate(${d.x},${d.y})`)
        .selectAll("circle.inner")
        .data(d => d);
    
    // Remove old small circles
    innerJoin
    	.exit()
        .transition()
        .duration(500)
        .attr("r", 0);
        
     const newCircles = innerJoin
     	 .enter()
         .append("circle")
         .attr("class", "inner")
         .attr("r", 0)
         .style("fill", "orange")
         .attr("cy", -RADIUS - 5);
         
     newCircles
     	.transition()
        .duration(500)
        .attr("r", 5);
    
     newCircles.merge(join)
         .attr("cx", (d, i) => 2 * i * 5);
}

const force = d3.forceSimulation()
    .nodes(data)
    .force("charge", d3.forceCollide().radius(100))
    .on("tick", () => {
        const container = d3.select(".container");
        container
        	.selectAll(".outer")
        	.attr("transform", d => `translate(${d.x},${d.y})`);
    });

render(data);

setTimeout(() => {
   data.push([ "circle10", "circle11", "circle12", "circle13"]);
   data[0].push("A");
   data[0].push("B");
   data[0].push("C");
  ...