简体   繁体   中英

Drawing an HTML table via D3 doesn't update existing data

I'm using D3 to draw an HTML table, and things work great on enter. When I add a new item to my data collection, it adds the new item to the table correctly.

The problem is whenever I update an existing object (an object in the backgroundJobs collection below) within the collection. When I re-run the D3 code to sync up the table, it does not work. Nothing happens.

Here's the code:

var visibleColumns = ['Name', 'Start', 'End', 'Status', 'Metadata', 'Errors'];

var table = d3.select('#jobs').append('table');
var thead = table.append('thead');
var tbody = table.append('tbody');

thead.append("tr")
    .selectAll("th")
    .data(visibleColumns)
    .enter()
    .append("th")
    .text(function (column) { return column; });

function tick() {
    var rows = tbody.selectAll("tr")
        .data(backgroundJobs, function(d) {
            return d.name;
        })
        .enter()
        .append("tr");

    var cells = rows.selectAll("td")
        .data(function(row) {
            return [{column: 'Name', value: row.name},
                    {column: 'Start', value: row.startedTimestamp},
                    {column: 'End', value: row.endedTimestamp},
                    {column: 'Status', value: row.isRunning},
                    {column: 'Metadata', value: ''},
                    {column: 'Errors', value: row.errorMsg}];
        })
       .enter()
        .append("td")
        .text(function(d) { return d.value; });
}

setInterval(tick, 500);

Please refer to the cool explanation of the data joins .

When you call

tbody.selectAll("tr").data(some-new-data);

You actually get 3 selections: 'enter' (whith the new elements not present in the DOM yet), 'exit' (those present in DOM but not longer present in the data) and 'update' which contains nodes that are already in DOM and still have the data assigned to them via the .data call above.

In general, for 'enter' selection you create new nodes, for 'exit' you need to remove the old ones, and for 'update' you just change attributes - may be with some nice transition effect. See the updated 'tick' function code.

function tick() {
    var rows = tbody.selectAll("tr")
    .data(backgroundJobs, function(d) {
        return d.name;
    });

    rows.enter()
        .append("tr");

    rows.order();

    var cells = rows.selectAll("td")
        .data(function(row) {
            return [{column: 'Name', value: row.name},
                {column: 'Start', value: row.startedTimestamp},
                {column: 'End', value: row.endedTimestamp},
                {column: 'Status', value: row.isRunning},
                {column: 'Metadata', value: ''},
                {column: 'Errors', value: row.errorMsg}];
        });

    cells.enter()
        .append("td");

    cells.text(function(d) { return d.value;});

    cells.exit().remove();

    rows.exit().remove();
}

See the Demo (backgroundJobs is switched on timer between the two hard-coded datasets).

The rows variable will be a selection that will only contains nodes if enter() is not empty. The second time around, if you haven't added any new rows into backgroundJobs , the data bind will update the existing nodes and enter() won't contain any nodes (meaning rows won't contain any nodes).

You can get around this by holding a reference to the update selection taking advantage of the fact that nodes appended to the enter selection get added to the update selection behind the scenes:

var rows = tbody.selectAll("tr")
    .data(backgroundJobs, function(d) {
        return d.name;
    });

rows.enter()
    .append("tr");

Now rows will refer to a selection that contains all previously existing and newly added nodes.

In order to be able to add and delete rows dynamically, this strategy works for me:

// ROW MANAGEMENT

// select all tr elements
var tr = this.table.select("tbody").selectAll("tr").data(cur_data);

// exit rows
tr.exit().remove();

// enter rows
var trEnter = tr.enter().append("tr");

// CELL MANAGEMENT

trEnter.selectAll("td").data(function(d) { return d3.values(d); }).enter().append("td").text(function(d) { return d; });

// select all td elements
var td = tr.selectAll("td").data(function(d) { return d3.values(d); });

// exit cells
td.exit().remove();

// enter and update/add data to cells
td.enter().append("td");
td.text(function(d) { return d; });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM