Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating Nested Data

Tags:

d3.js

I'm trying to figure out how to update nested data in a D3 application where the nesting is greater than 2 levels deep. Let's start out with my code.

Here's the jQuery handler when the page is ready.

$(function () {
    var data = createDataSet();
    updateD3(data);

    setTimeout(function () {
        var dataAgain = createDataSet();
        updateD3(dataAgain);
    }, 5000);
});

Here's the update function.

function updateD3(data) {
    var level1 = d3
        .select('body')
        .selectAll('div.level-1')
        .data(data);

    // UPDATE existing data.
    level1
        .text(function (d) { return d.nameLevelOne + '-'; })
        .selectAll('div.level-2')
        .data(function (d) { return d.valuesLevelOne; })
        .text(function (d) { return d.nameLevelTwo + '-'; })
        .selectAll('div.level-3')
        .data(function (d) { return d.valuesLevelTwo; })
        .text(function (d) { return d.nameLevelThree + ' ' + d.count + '-'; });

    // CREATE new data.
    level1
    .enter()
        .append('div')
        .attr('class', 'level-1')
        .text(function (d) { return d.nameLevelOne; })
        .selectAll('div')
        .data(function (d) { return d.valuesLevelOne; })
    .enter()
        .append('div')
        .attr('class', 'level-2')
        .text(function (d) { return d.nameLevelTwo; })
        .selectAll('div')
        .data(function (d) { return d.valuesLevelTwo; })
    .enter()
        .append('div')
        .attr('class', 'level-3')
        .text(function (d) { return d.nameLevelThree + ' ' + d.count; });

    // REMOVE deleted data.
    level1
    .exit()
        .selectAll('div')
        .transition()
        .style('opacity', 0)
        .remove();
}

Here's the data creation function, which also documents the structure of the data.

function createDataSet() {
        return [
        {
            nameLevelOne: 'A',
            valuesLevelOne: [{
                nameLevelTwo: 'AA',
                valuesLevelTwo: [
                    {
                        nameLevelThree: 'AAA',
                        count: Math.random() * 100
                    }
                ]
            }]
        },
        {
            nameLevelOne: 'B',
            valuesLevelOne: [{
                nameLevelTwo: 'BB',
                valuesLevelTwo: [
                    {
                        nameLevelThree: 'BBB',
                        count: Math.random() * 100
                    }
                ]
            }]
        }
    ];
}

When I run this code in the browser, the first updateD3() call is perfect. It shows this:

A
AA
AAA 24.26636815071106
B
BB
BBB 37.17236865777522

When 5 seconds pass and a new data set is created and passed into another call to updateD3(), the output changes to this:

A-
B-

This is NOT the output I expected. I did not expect any part of the DOM to be destroyed. Essentially, I expected the numbers by AAA and BBB to be updated to new values.

Why is this happening? How do I only update the DOM with the updated data?

I have looked and looked around the Internet for solutions but nearly every example of "nested" data is actually only 2 levels deep, or if the data is more than 2 levels deep the example has no code demonstrating updating data.

Any help is greatly appreciated, especially after around 6 hours of beating my head against the wall over this.

like image 509
kevin628 Avatar asked Jul 09 '26 19:07

kevin628


1 Answers

You need to make the selection at each level once instead of multiple times, i.e. something like

var level1 = selection.data(...);
level1.enter()...
level1.exit()...
level1....

var level2 = level1.selectAll(...).data(...);
level2.enter()...
level2.exit()...
level2...

var level3 = level2.selectAll(...).data(...);
// etc

When you're making the selections and binding data multiple times, the selections affect each other and you get wrong elements in the selections.

like image 200
Lars Kotthoff Avatar answered Jul 13 '26 15:07

Lars Kotthoff