首页 > 解决方案 > D3:重入表函数

问题描述

我还是 Javascript 和 D3 的新手,我用它来创建下面的表格函数。相应地,我做了一个fiddle

function tabulate(head,body) {
  // Select Tag to manipulate
  var table = d3.select("table");
  // Create Head
  var thead = table.selectAll("thead").data([null]);
  thead.enter().append("thead");
  // Populate Head
  var tr = thead.selectAll('tr').data([head]);
  tr.enter().append("tr");
  var th = tr.selectAll('th').data(head);
  th.enter().append("th").text(function(d) { return d; });
  // Create Body
  var tbody = table.selectAll("tbody").data([null]);
  tbody.enter().append("tbody");
  // Populate Body
  var tr = tbody.selectAll('tr').data(body);
  tr.enter().append('tr');
  var td = tr.selectAll('td')
          .data(
            function (body) {
             return head.map(function (item) { return {col: item, row: body[item]}; });
          });
  td.enter().append('td')
    .text(function (d) { return d.row; });
};

该函数旨在用必要的子结构(thead/tbody/tr 标签)和提供的数据替换现有表的内容,但我得到了一些奇怪的行为。一方面,除非该函数被调用了几次,否则内容无法正确显示;在显示任何数据之前,似乎必须在这些连续调用期间构建子结构。其次,它无法在一次调用中正确交换表数据。

我应该将子结构结构嵌套在.call链中,还是应该在这里完全做其他事情?

鉴于以下数据

const data = [{"odd":1,"even":2},{"odd":3,"even":4}];

我必须调用该函数三次

tabulate(["even"], data); // Adds thead/tbody
tabulate(["even"], data); // Adds tr
tabulate(["even"], data); // Adds th/td elements (Finally)

生成所需的结构

<table>
  <thead>
     <tr><th>even</th></tr>
  </thead>
  <tbody>
    <tr><td>2</td></tr>
    <tr><td>4</td></tr>
  </tbody>
</table>

我已经阅读并改编了blocks.org上的一些 D3 示例,我觉得我了解 D3 使用的 Join+Create/Update/Delete 模式。我还阅读了D3 网站上第 91 期下讨论“新”.merge方法的评论。Bostock 先生提到这里可以使用控制流而不是连接。

标签: javascriptd3.js

解决方案


关于 d3 需要注意的一点是,您可以将它用于“vanilla”(普通)DOM 操作;您不必每次想要创建或操作 DOM 元素时都绑定数据。null您可以简单地将其附加到现有元素,而不是尝试通过绑定来创建元素:

function tabulate(head,body) {
  // Select Tag to manipulate
  var table = d3.select("table");

  // Old code:
  // var thead = table.selectAll("thead").data([null]);
  // thead.enter().append("thead");
  // var tr = thead.selectAll('tr').data([head]);
  // tr.enter().append("tr");

  var tr = table.append('thead').append('tr');

除非您希望或需要将数据绑定到某个项目,否则最好不要尝试将假数据(包含 的数组null)或未使用的大块数据(数组)绑定head到元素。在这种情况下,应该有数据绑定到它们的表的相关部分,如果显示不同的数据集,可能会发生变化,是表头单元格 ( th) 和表体的内容,行 ( trs) 表示每个数据和包含值的表格单元格 ( tds)。因此,像这样编写表格代码会更有效:

function tabulate(head,body) {
     // Select Tag to manipulate
    var table = d3.select("table")

    // Populate table head
    var th = table.append('thead').append('tr')
      // each th element has an item from the array `head` bound to it
      .selectAll('th').data(head)
      .enter()
        .append('th')
        .text(function(d){ return d; })

    // Populate table body
    var tr = table.append("tbody")
      // each table row has one item from array `body` bound to it
      .selectAll('tr').data(body)
        .enter()
          .append('tr');

    // transform each datum
    var td = tr.selectAll('td').data(function (d) {
            return head.map(function (item) {
              return {col: item, row: d[item]};
            });
         })
        .enter()
          .append('td')
          .text(function (d) { return d.row; });
}

查看更新的小提琴


推荐阅读