这是key 函数的目的,它是selection.data的第二个参数:它允许您通过控制哪个数据绑定到哪个元素来保持对象的恒定性。例如,假设您有一个代表水果的对象数组:
var fruits = [
{name: "orange", value: 200},
{name: "apple", value: 124},
{name: "banana", value: 32}
];
当您第一次创建元素时,您不需要 key 函数,因为没有任何现有元素,因此您可以使用标准 selectAll-data-enter-append 模式:
var div = d3.select("body").selectAll(".fruit")
.data(fruits)
.enter().append("div")
.attr("class", "fruit")
.text(function(d) { return d.name + ": " + d.value; });
此操作的结果是:
<body>
<div class="fruit">orange: 200</div>
<div class="fruit">apple: 124</div>
<div class="fruit">banana: 32</div>
</body>
但是现在假设您的数据发生了变化,并且您想要更新以反映新数据。这些新数据的顺序可能不同,并且可能会添加或删除一些元素:
var fruits2 = [
{name: "apple", value: 124},
{name: "lemon", value: 17},
{name: "banana", value: 32},
{name: "strawberry", value: 1465}
];
苹果和香蕉在原始数据中,所以我们想保留它们。这称为更新选择。草莓和柠檬是新的;这是输入选择。最后橙色消失了,形成了退出选择。
使用引用name
数据属性的键函数,我们可以按预期将新数据分配给旧元素。然后我们可以创建进入的水果并移除退出的水果:
var div = d3.select("body").selectAll(".fruit")
.data(fruits2, function(d) { return d.name; });
div.enter().append("div")
.attr("class", "fruit")
.text(function(d) { return d.name + ": " + d.value; });
div.exit().remove();
这称为一般更新模式。(你可以在第一次这样做;它是 selectAll-data-enter-append 模式的更通用形式。)结果是:
<body>
<div class="fruit">apple: 124</div>
<div class="fruit">banana: 32</div>
<div class="fruit">lemon: 17</div>
<div class="fruit">strawberry: 1465</div>
</body>
虽然选择的顺序div
与 data 匹配fruit2
,但 DOM 中的顺序不匹配,因为输入元素被附加到正文的末尾。(selection.append 没有任何花哨的逻辑来保证顺序;它只是将新元素附加到父元素的末尾。)使用 SVG 时,我们通常不关心 DOM 元素的顺序,因为所有内容都是绝对定位的。如果您确实关心订单,则可以在输入后使用selection.order修复它:
div.order(); // make the DOM element order match the selection
此外,在本例中,我们不需要对更新选择进行任何更改,因为苹果和香蕉的值没有改变。如果更新数据也发生变化,您可以直接从上面的 selection.data 调用中对 selection.attr 和 selection.text 进行链式调用。