ColinE
ColinE

Reputation: 70122

D3 accessing the update selection for nested datajoins

I'm rendering a table of data using D3, and have successfully managed to make the initial render work:

var data = [
  [1,2,3],
  [1,2,3],
  [1,2,3]
];

render(data);

function render(data) {
  var container = d3.select('#container');
  var table = container.select('table');
  if (!table.node()) {
      table = container.append('table')
          .classed('legend', true);
  }

  // create the row selection
  var tr = table.selectAll('tr')
      .data(data);

  // append 'tr' on enter
  tr.enter()
      .append('tr');

  // create the cell selection
  var td = tr.selectAll('td')
      .data(function(d) { return d; });

  // append on enter
  td.enter()
    .append('td');

  // update cell text on update
  td.text(function(d) {
      return d;
  });
}

The above code creates a table if one isn't presented (to avoid having a triple nested join!), it creates a datajoin for the rows, then a nested datajoin for the cells.

With the above code, the initial data renders just fine.

However, if I updated the data as follows:

setTimeout(function() {
  for (var i = 0; i < 3; i++) {  
    for(var j = 0; j < 3; j++) {
      data[i][j] = data[i][j] + Math.random();
    }
  }
  render(data);
}, 500);

I would expect that the last statement which is the td update selection to be invoked each time:

  td.text(function(d) {
      return d;
  });

Unfortunately, this is not the case!

I've seen examples that show enter / exit / update for simple datajoins (e.g http://bost.ocks.org/mike/join/), and also examples for nested datajoins.

However, I've not seen an example that demonstrates enter / exit / update for a nested datajoin. Can anyone see what is wrong with my above code and why the td update selection is not being called on subsequent 'render' steps?

JSBin: http://jsbin.com/xoqaxayamu/edit?html,output

Upvotes: 1

Views: 496

Answers (1)

Cool Blue
Cool Blue

Reputation: 6476

To murder the metaphor even more, you shouldn't have to being doing a tap dance routine like this in d3...

  var table = container.select('table');
  if (!table.node()) {
      table = container.append('table')
          .classed('legend', true);
  }  

Here is the idiomatic way to do it...

var data = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

render(data);
render(data);
render(data);

function render(data) {
  var container = d3.select('#container');
  var table = container.selectAll('table')
    .data([data]);
  table.enter().append("table").classed("legend", true)
  table.exit().remove();
  // create the row selection
  var tr = table.selectAll('tr')
    .data(function(d) {
      return d
    });
  tr.exit().remove();
  // append 'tr' on enter
  tr.enter()
    .append('tr');

  // create the cell selection
  var td = tr.selectAll('td')
    .data(function(d) {
      return d;
    });
  td.exit().remove();
  // append on enter
  td.enter()
    .append('td');

  // update cell text on update
  td.text(function(d) {
    return d3.format(" >8,.3f")(d);
  });

}
setInterval(function() {
  for (var i = 0; i < 3; i++) {  
    for(var j = 0; j < 3; j++) {
      data[i][j] = data[i][j] + Math.random();
    }
  }
  render(data);
}, 500);
body {
      white-space: pre;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="container"></div>

Upvotes: 1

Related Questions