selection.data
函数的描述包括一个包含多个组(link)的示例,其中二维数组转换为HTML表。
在d3.js v3中,对于较低维度,访问者函数包括第三个参数,该参数是父组基准的索引:
td.text(function(d,i,j) {
return "Row: " + j;
});
在v4中,这个j
参数已被选择的NodeList取代。如何立即访问父组的基准索引?
答案 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;
});