Search code examples
d3.jsdata-bindingdata-visualization

D3 updates every path only when select() is running instead of selectAll()


I'm trying to update chord diagram according to the changes in data.

I created groups for each element and updated the data binding. However, for some reason, it updates the data accordingly, only when I go with 'select' instead of 'selectAll' which feels quite odd for me because everytime I update data binding I have used selectAlll only to update every element related.

My code is as below.

-Creating initial diagram-

var g = svg.selectAll('g.groups')
    .data(figureCalculation.groups)
    .join('g')
    .attr('class', (d, i) => { return `group ${nameArray[i]}` })




g.append('path')
    .attr('class', (d) => { return `arc ${d.value}` })
    .attr('d', arc)
    .style('fill', 'grey')
    .style('stroke', 'pink')
    .style("opacity", 0)
    .transition().duration(1000)
    .style("opacity", 0.8);



var chords = svg.append('g')
    .selectAll('path')
    .data(figureCalculation.chords)
    .join('path')
    .attr('class', 'chords')
    .attr('d', d3.ribbon().radius(innerRadius))
    .style('fill', 'green')
    .style('stroke', 'red')

-update the data binding-

setTimeout(updateData, 2500)

function updateData() {


    figureCalculation = d3.chord()
        .padAngle(0.05)
        .sortSubgroups(d3.descending)(matrix1)


    figureCalculation.chords = [];
    figureCalculation.forEach((d) => {
        figureCalculation.chords.push(d)
    })

    g.select('path').data(figureCalculation.groups)
        .join('path').attr('d', arc)
        .style('fill', 'grey')
        .style('stroke', 'pink')

    chords.select('path').data(figureCalculation.chords)
        .join('path')
        .attr('d', d3.ribbon().radius(innerRadius))
        .style('fill', 'green')
        .style('stroke', 'red')

}

The full code is in the following link.

https://codepen.io/jotnajoa/pen/qBaXKVW


Solution

  • Your SVG is structured strangely.

    First, for your groups, you create a g with one child of path. Your update doesn't work because you do a selectAll of paths on the g with only one child.

    Then for your chords that variable is already a collection of path. You are treating it like it's the g element holding the path.

    I'd rewrite your code like this:

    let margin = { top: 50, bottom: 50, left: 20, right: 20 }
    
    let width = 600 - margin.left - margin.right;
    let height = 600 - margin.top - margin.bottom;
    let innerRadius = Math.min(width, height) * 0.4;
    let outterRadius = innerRadius * 1.2
    let svg = d3.select('#graph').append('svg')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .attr('transform', `translate(${width / 2 + margin.left}, ${height / 2 + margin.top})`)
    
    
    var nameArray = ['A', 'B', 'C', 'D'];
    var matrix = [
        [11975, 5871, 8916, 2868],
        [1951, 10048, 2060, 6171],
        [8010, 16145, 8090, 8045],
        [1013, 990, 940, 6907]
    ];
    
    var matrix1 = [
        [175, 571, 916, 868],
        [1951, 1248, 2060, 5471],
        [8010, 14145, 4390, 4245],
        [1213, 990, 540, 1207]
    ];
    
    
    
    let figureCalculation = d3.chord()
        .padAngle(0.05)
        .sortSubgroups(d3.descending)(matrix)
    
    
    figureCalculation.chords = [];
    figureCalculation.forEach((d) => {
        figureCalculation.chords.push(d)
    })
    
    
    
    var arc = d3.arc().innerRadius(innerRadius).outerRadius(outterRadius)
    
    svg
        .append('g')
        .attr('class', 'groups')
        .selectAll('path')
        .data(figureCalculation.groups)
        .join('path')
        .attr('class', (d, i) => { return `group ${nameArray[i]}` })
        .attr('d', arc)
        .style('fill', 'grey')
        .style('stroke', 'pink')
        .style("opacity", 0)
        .transition().duration(1000)
        .style("opacity", 0.8);
    
    svg
        .append('g')
        .attr('class', 'chords')
        .selectAll('path')
        .data(figureCalculation.chords)
        .join('path')
        .attr('class', 'chords')
        .attr('d', d3.ribbon().radius(innerRadius))
        .style('fill', 'green')
        .style('stroke', 'red')
    
    function updateData() {
    
        figureCalculation = d3.chord()
            .padAngle(0.05)
            .sortSubgroups(d3.descending)(matrix1)
    
        figureCalculation.chords = [];
        figureCalculation.forEach((d) => {
            figureCalculation.chords.push(d)
        })
    
        svg.select('.groups')
            .selectAll('path').data(figureCalculation.groups)
            .join('path').attr('d', arc)
            .style('fill', 'grey')
            .style('stroke', 'pink')
    
        svg.select('.chords')
            .selectAll('path').data(figureCalculation.chords)
            .join('path')
            .attr('d', d3.ribbon().radius(innerRadius))
            .style('fill', 'green')
            .style('stroke', 'red')
    
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
    <button onclick="updateData()">Update Data</button>
    <div id="graph"></div>