d3.js v4:如何访问父组的基准索引?

时间:2016-07-06 20:16:57

标签: d3.js

selection.data函数的描述包括一个包含多个组(link)的示例,其中二维数组转换为HTML表。

在d3.js v3中,对于较低维度,访问者函数包括第三个参数,该参数是父组基准的索引:

td.text(function(d,i,j) {
  return "Row: " + j;
});

在v4中,这个j参数已被选择的NodeList取代。如何立即访问父组的基准索引?

7 个答案:

答案 0 :(得分:16)

好吧,有时答案不提供解决方案,因为解决方案可能不存在。情况似乎如此。

根据博斯托克的说法:

  

我已将新的双层选择实现合并到master中,并简化了如何使用并行父阵列跟踪父项。

     

这种新方法的一个很好的属性是selection.data可以   以与其他方式完全相同的方式评估值函数   选择函数:值函数传递{d,i,nodes}   这是父节点,d是父数据,i是父节点   (组)索引,节点是父节点的数组(每组一个)。   此外,父数组可以通过不具有的子选择重用   重新组合选择,例如selection.select,因为父母   数组是不可变的。

     

此更改会限制功能 - 在 无法 的意义上   从选择函数中访问父节点,也不是   父数据, 也不是群组索引 - 但我相信这最终是A   好事,因为它鼓励更简单的代码。

(强调我的)

以下是链接:DateTimeFormatter

因此,无法使用selection获取父组的索引(可以使用selection.data检索父组的索引,如下面的代码段所示。)

var testData = [
[
  {x: 1, y: 40},
  {x: 2, y: 43},
  {x: 3, y: 12},
  {x: 6, y: 23}
], [
  {x: 1, y: 12},
  {x: 4, y: 18},
  {x: 5, y: 73},
  {x: 6, y: 27}
], [
  {x: 1, y: 60},
  {x: 2, y: 49},
  {x: 3, y: 16},
  {x: 6, y: 20}
 ] 
];

var svg = d3.select("body")
	.append("svg")
  .attr("width", 300)
  .attr("height", 300);
  
var g = svg.selectAll(".groups")
    .data(testData)
    .enter()
    .append("g");
    
var rects = g.selectAll("rect")
    .data(function(d, i , j) { console.log("Data: " + JSON.stringify(d), "\nIndex: " + JSON.stringify(i), "\nNode: " + JSON.stringify(j)); return d})
    .enter()
    .append("rect");
<script src="https://d3js.org/d3.v4.min.js"></script>

答案 1 :(得分:6)

我的解决方法与Dinesh Rajan有些相似,假设someAttr的属性g.nestedElt需要父索引:

<强> V3

svg.selectAll(".someClass")
    .data(nestedData)
  .enter()
  .append("g")
    .attr("class", "someClass")
  .selectAll(".nestedElt")
    .data(Object)
  .enter()
  .append("g")
    .attr("class", "nestedElt")
    .attr("someAttr", function(d, i, j) {

    });

<强> V4

svg.selectAll(".someClass")
    .data(nestedData)
  .enter()
  .append("g")
    .attr("class", "someClass")
    .attr("data-index", function(d, i) { return i; }) // make parent index available from DOM
  .selectAll(".nestedElt")
    .data(Object)
  .enter()
  .append("g")
    .attr("class", "nestedElt")
    .attr("someAttr", function(d, i) {
      var j = +this.parentNode.getAttribute("data-index");
    });

答案 2 :(得分:4)

我最终定义了一个外部变量“j”,然后每当“i”为0

时递增它

示例 V3 以下代码段。

rowcols.enter().append("rect")
 .attr("x", function (d, i, j) { return CalcXPos(d, j); })
 .attr("fill", function (d, i, j) { return GetColor(d, j); })

并在 V4 中,代码转换如下。

var j = -1;
rowcols.enter().append("rect") 
 .attr("x", function (d, i) { if (i == 0) { j++ }; return CalcXPos(d, j); })
 .attr("fill", function (d, i) { return GetColor(d, j); })

答案 3 :(得分:2)

如果j是nodeList ...

  • j[i]是当前节点(例如,td元素)
  • j[i].parentNode 1级父 (例如,行元素)
  • j[i].parentNode.parentNode 2级家长 (例如表格元素)

  • j[i].parentNode.parentNode.childNodes数组1级父级 (例如,行元素数组),包括原始父级。

所以问题是,父母(行)相对于父母(表格)的索引是什么?

我们可以使用Array.prototype.indexOf像这样找到...

k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);

您可以在下面的代码段中看到,在返回td时,每个k单元格中都会打印该行。

var testData = [
[
  {x: 1, y: 1},
  {x: 1, y: 2},
  {x: 1, y: 3},
  {x: 1, y: 4}
], [
  {x: 2, y: 1},
  {x: 2, y: 2},
  {x: 2, y: 3},
  {x: 2, y: 4}
], [
  {x: 3, y: 4},
  {x: 3, y: 4},
  {x: 3, y: 4},
  {x: 3, y: 4}
 ]
];

var tableData =
  d3.select('body').selectAll('table')
    .data([testData]);

var tables =
  tableData.enter()
  .append('table');

var rowData =
  tables.selectAll('table')
    .data(function(d,i,j){
      return d;
    });

var rows =
  rowData.enter()
  .append('tr');

var eleData =
  rows.selectAll('tr')
    .data(function(d,i,j){
      return d;
    });

var ele =
  eleData.enter()
  .append('td')
    .text(function(d,i,j){
      var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes,j[i].parentNode);
      return k;
    });
<script src="https://d3js.org/d3.v4.min.js"></script>

<强>预订

此方法使用DOM顺序作为数据索引的代理。在许多情况下,我认为这是一个可行的创可贴解决方案,如果在D3中不再可能(如this answer中所述)。

可能需要在操作DOM选择以匹配数据方面做出一些额外的努力。例如,仅为了确定行而对j[i].parentNode.parentNode.childNodes元素进行过滤<tr> - 通常说childNodes数组可能与选择不匹配,并且可能包含额外的元素/垃圾。

虽然这不是万灵药,但我认为它应该可以工作或者可以在大多数情况下工作,假设DOM和数据之间存在一些可以利用的逻辑连接,允许您使用DOM子索引作为数据索引的代理。

答案 4 :(得分:2)

以下是如何使用selection.each()方法的示例。我认为它不是很混乱,但它确实减慢了大矩阵上的渲染速度。请注意,以下代码假定现有table选项和对update()的调用。

update(matrix) {
        var self = this;
        var tr = table.selectAll("tr").data(matrix);

        tr.exit().remove();

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

        tr.each(addCells);

        function addCells(data, rowIndex) {
            var td = d3.select(this).selectAll("td")
                .data(function (d) {
                    return d;
                });
            td.exit().remove();

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

            td.attr("class", function (d) {
                return d === 0 ? "dead" : "alive";
            });

            td.on("click", function(d,i){
                matrix[rowIndex][i] = d === 1 ? 0 : 1; // rowIndex now available for use in callback.                   
            });
        }

        setTimeout(function() {
            update(getNewMatrix(matrix))
        }, 1000);
    },

答案 5 :(得分:2)

假设你想做一个嵌套的选择,你的 数据是一些数组,其中每个元素依次 包含一个数组,让我们说&#34;值&#34;。那么你 可能有这样的代码:

var aInnerSelection = oSelection.selectAll(".someClass") //
    .data(d.values) //
    ...

您可以使用新数组替换数组,其中 你缓存组内的索引。

var aInnerSelection = oSelection.selectAll(".someClass") //
    .data(function (d, i) {
        var aData = d.values.map(function mapValuesToIndexedValues(elem, index) {
                return {
                    outerIndex: i,
                    innerIndex: index,
                    datum: elem
                };
            })
            return aData;
    }, function (d, i) {
        return d.innerIndex;
    }) //
    ...

假设您的外部数组如下所示: [{name&#34; X&#34;,值:[&#34; A&#34;,&#34; B&#34;]},{name&#34; y&#34;,值:[& #34; C&#34;,&#34; D&#34;]}
使用第一种方法,嵌套选择将带您从这里

                  d                                i
------------------------------------------------------------------
root dummy X      {name "X", values: ["A", "B"]}    0
     dummy Y      {name "Y", values: ["C", "D"]}    1

到这里。

             d       i
------------------------------------------------------------------
root X  A    "A"     0
        B    "B"     1
     Y  C    "C"     2
        D    "D"     3

使用增强数组,您最终会转到此处:

             d                                              i
------------------------------------------------------------------
root X  A    {datum: "A", outerIndex: 0, innerIndex: 0}     0
        B    {datum: "B", outerIndex: 0, innerIndex: 1}     1
     Y  C    {datum: "C", outerIndex: 1, innerIndex: 0}     2
        D    {datum: "D", outerIndex: 1, innerIndex: 1}     3

所以你在嵌套选​​择中,在任何函数(d,i)中都有 你需要的信息。

答案 6 :(得分:0)

我的解决方案是将此信息嵌入提供给d3js的数据中

data = [[1,2,3],[4,5,6],[7,8,9]]
flattened_data = data.reduce((acc, v, i) => {
    v.forEach((d, j) => {
      data_item = { i, j, d };
      acc.push(data_item);
    });
    return acc;
  }, []);

然后您可以从函数的数据arg访问i,j和d

td.text(function(d) {
  // Can access i, j and original data here
  return "Row: " + d.j;
});