D3:重入表格功能

时间:2018-09-03 23:49:44

标签: javascript d3.js

我还是Java和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网站上Issue 91下的有关“新” .merge方法的评论。博斯托克先生提到,有人可能会对联接here使用控制流。

2 个答案:

答案 0 :(得分:0)

关于d3,需要注意的一件事是,您可以将其用于“普通”(普通)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的数组)或未使用的大数据块({ {1}}数组)到一个元素。在这种情况下,表的顶部单元格(head)以及表主体的内容,表示每个基准的行(th s)和包含值的表单元格(tr s)。因此,这样编写表代码会更有效:

td

请参见the updated fiddle

答案 1 :(得分:0)

在阅读了一堆其他文章之后,我发现了一个神秘的摘要,暗示了此page底部的解决方案。我仍然没有完整的解决方案,因为我必须按如下所示修改容器元素,

<table>
 <thead></thead>
 <tbody></tbody>
</table>

但部分解决方案不能胜过解决方案,并暗示了通用模式。 更新后的Javascript如下:

function tabulate(head, body) {
  // Enclosing Tag
  var table = d3.select("table");
  // Head
  var tr = table.select("thead").selectAll("tr").data([head])
  var trDelete = tr.exit().remove();
  var trUpdate = tr.enter().append("tr").merge(tr);
  // tr.each(th);
  var th = trUpdate.selectAll("th").data(function (d,i) {return d;});
  var thDelete = th.exit().remove();
  var thUpdate = th.enter().append("th").merge(th);
  thUpdate.text(function (d) {return d;});
  // Body
  var tr = table.select("tbody").selectAll("tr").data(body);
  var trDelete = tr.exit().remove();
  var trMerged = tr.enter().append("tr").merge(tr);
  var td = trMerged.selectAll("td")
      .data(function (body) {
           return head.map(function (item) { return body[item] });});
  td.enter()  
    .append('td')
    .merge(td)
    .text(function (d) { return d; });
 }

看来,技巧是将初始选择table.select("thead|tbody").selectAll("TAG")的“创建”和“更新”子集与.merge()绑定在一起(请参阅trUpdate分配)。然后在后续操作中操作组合/合并的集合。这样做会在单个调用中生成tr子结构。感谢@i惊慌的外星人为他/她/它提供的帮助,“批发替换” quib使我思考正确的道路,否则我现在会以锋利的器具或一些绳索变得节俭。其他使我敬酒的信息是nested selectionsheterogenous nesting文章。