如果我有一个JSON对象数组。 D3如何确定enter()
集合中的哪些内容?
如果我有一个对象数组,如下:
var data = [
{label:'a', value:1},
{label:'b', value:3},
{label:'c', value:2}
]
然后我将它绑定到选择:
var bars = vis.selectAll("rect.bar")
.data(data)
enter
集现在包含所有这些数据。如果我现在更改数据:
var data = [
{label:'a', value:99},
{label:'c', value:2}
{label:'b', value:3},
]
D3如何判断哪些值是更新,移动还是新的?它是在对象身份上完成的吗?有没有办法让它使用标签字段来填充输入集?
答案 0 :(得分:6)
如果我正确理解您的问题,您想知道D3如何将给定对象与给定DOM元素相关联。
默认情况下,如果您没有设置关键功能(下面有更多内容),则对象会按其顺序关联。根据API:
...数据中的第一个数据分配给第一个选定元素,第二个数据分配给第二个选定元素,依此类推。
让我们在下面的例子中看到这一点。第一个数组是:
var data = [{
label: 'a',
value: 1
}, {
label: 'b',
value: 3
}, {
label: 'c',
value: 2
}];
第二个数组也有3个对象。但请注意更改:第一个是标签c
,第三个是标签a
。他们被交换了:
var data2 = [{
label: 'c',
value: 99
}, {
label: 'b',
value: 2
}, {
label: 'a',
value: 3
}];
因此,第一个数据中相对于a
的条形将在第二个数据中获得c
的值。查看演示,单击按钮:
var svg = d3.select("svg");
var data = [{
label: 'a',
value: 1
}, {
label: 'b',
value: 3
}, {
label: 'c',
value: 2
}];
var data2 = [{
label: 'c',
value: 99
}, {
label: 'b',
value: 2
}, {
label: 'a',
value: 3
}];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, 260]);
var yScale = d3.scaleBand()
.domain(data.map(d => d.label))
.range([10, 140])
.padding(0.3);
var rects = svg.selectAll("foo")
.data(data)
.enter()
.append("rect");
rects.attr("x", 40)
.attr("y", d => yScale(d.label))
.attr("height", yScale.bandwidth)
.attr("width", d => xScale(d.value))
.attr("fill", "teal");
d3.axisLeft(yScale)(svg.append("g").attr("transform", "translate(40,0)"))
d3.select("button").on("click", function() {
xScale.domain([0, d3.max(data2, d => d.value)]);
rects.data(data2);
rects.transition()
.duration(1000)
.attr("width", d => xScale(d.value))
})

<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click</button>
<br>
<svg></svg>
&#13;
如您所见,单击按钮后的图表显然不正确。
为避免这种情况,确保先前绑定到标签a
的DOM元素在数据数组更改时仍将绑定到该标签a
,我们必须使用键函数:< / p>
可以指定一个键函数来控制将哪个数据分配给哪个元素,替换默认的join-by-index。为每个选定的元素评估此关键函数,按顺序传递当前数据(d),当前索引(i)和当前组(节点),将其作为当前DOM元素(nodes [i]) 。然后还为数据中的每个新数据评估关键函数,传递当前数据(d),当前索引(i)和组的新数据,并将其作为组的父DOM元素。给定键的数据被分配给具有匹配键的元素。如果多个元素具有相同的键,则将重复的元素放入出口选择中;如果多个数据具有相同的密钥,则将重复数据放入回车选择中。
在您的情况下,这将是关键功能:
.data(data, d => d.label)
//key-------^
检查完全相同的代码,但使用关键功能:
var svg = d3.select("svg");
var data = [{
label: 'a',
value: 1
}, {
label: 'b',
value: 3
}, {
label: 'c',
value: 2
}];
var data2 = [{
label: 'c',
value: 99
}, {
label: 'b',
value: 2
}, {
label: 'a',
value: 3
}];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, 260]);
var yScale = d3.scaleBand()
.domain(data.map(d => d.label))
.range([10, 140])
.padding(0.3);
var rects = svg.selectAll("foo")
.data(data, d => d.label)
.enter()
.append("rect");
rects.attr("x", 40)
.attr("y", d => yScale(d.label))
.attr("height", yScale.bandwidth)
.attr("width", d => xScale(d.value))
.attr("fill", "teal");
d3.axisLeft(yScale)(svg.append("g").attr("transform", "translate(40,0)"))
d3.select("button").on("click", function() {
xScale.domain([0, d3.max(data2, d => d.value)]);
rects.data(data2, d => d.label);
rects.transition()
.duration(1000)
.attr("width", d => xScale(d.value))
})
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click</button>
<br>
<svg></svg>
&#13;
你可以看到,尽管新数据数组中对象的顺序不同(c
是第三个,现在c
是第一个) ,这并不重要,因为D3考虑到label
属性将数据绑定到每个DOM元素。