连接数据
有关介绍,请参见 使用连接进行思考 和 selection.join 笔记本。
selection.data(data, key)
源代码 · 将指定的 data 数组绑定到选定的元素,返回一个新的选择,表示 update 选择:成功绑定到数据的元素。还定义了返回选择上的 enter 和 exit 选择,它们可用于添加或删除元素以对应于新数据。当数据被分配给一个元素时,它会被存储在属性 __data__
中,从而使数据“粘性”并在重新选择时可用。
data 是针对选择中的每个组指定的。如果选择有多个组(例如,d3.selectAll 后面跟着 selection.selectAll),那么 data 通常应指定为一个函数。此函数将针对每个组按顺序进行计算,并传递组的父级数据项(d,它可能未定义)、组索引(i)以及选择的父级节点(nodes),其中 this 是组的父级元素。
结合使用 selection.join(或更明确地说,结合使用 selection.enter、selection.exit、selection.append 和 selection.remove),selection.data 可用于进入、更新和退出元素以匹配数据。例如,要从数字矩阵创建 HTML 表格
const matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
d3.select("body")
.append("table")
.selectAll("tr")
.data(matrix)
.join("tr")
.selectAll("td")
.data(d => d)
.join("td")
.text(d => d);
在此示例中,data 函数是恒等函数:对于每个表格行,它返回数据矩阵中相应的行。
如果未指定 key 函数,则 data 中的第一个数据项将分配给第一个选定元素,第二个数据项分配给第二个选定元素,依此类推。可以指定 key 函数来控制将哪个数据项分配给哪个元素,通过为每个数据项和元素计算一个字符串标识符来替换默认的按索引连接方式。此键函数将针对每个选定元素按顺序进行计算,并传递当前数据项(d)、当前索引(i)以及当前组(nodes),其中 this 是当前 DOM 元素(nodes[i]);返回的字符串是元素的键。然后,键函数也将针对 data 中的每个新数据项进行计算,并传递当前数据项(d)、当前索引(i)以及组的新 data,其中 this 是组的父级 DOM 元素;返回的字符串是数据项的键。具有给定键的数据项将分配给具有匹配键的元素。如果多个元素具有相同的键,则重复元素将放入 exit 选择中;如果多个数据具有相同的键,则重复数据将放入 enter 选择中。
例如,给定以下文档
<div id="Ford"></div>
<div id="Jarrah"></div>
<div id="Kwon"></div>
<div id="Locke"></div>
<div id="Reyes"></div>
<div id="Shephard"></div>
您可以按如下方式按键连接数据
const data = [
{name: "Locke", number: 4},
{name: "Reyes", number: 8},
{name: "Ford", number: 15},
{name: "Jarrah", number: 16},
{name: "Shephard", number: 23},
{name: "Kwon", number: 42}
];
d3.selectAll("div")
.data(data, function(d) { return d ? d.name : this.id; })
.text(d => d.number);
此示例键函数在存在数据项 d 时使用它,否则将回退到元素的 id 属性。由于这些元素以前未绑定到数据,因此当对选定元素计算键函数时,数据项 d 为 null,而当对新数据计算键函数时,它为非 null。
update 和 enter 选择按数据顺序返回,而 exit 选择保留连接之前的选择顺序。如果指定了键函数,则选择中元素的顺序可能与它们在文档中的顺序不匹配;根据需要使用 selection.order 或 selection.sort。有关键函数如何影响连接的更多信息,请参见 柱状图,第 2 部分 和 对象恒定性。
如果未指定 data,则此方法将返回选定元素的数据数组。
此方法不能用于清除绑定数据;请改用 selection.datum。
selection.join(enter, update, exit)
源代码 · 根据先前由 selection.data 绑定的数据,根据需要添加、删除和重新排序元素,并返回 合并 的 enter 和 update 选择。此方法是显式 一般更新模式 的便捷替代方案,它替换了 selection.enter、selection.exit、selection.append、selection.remove 和 selection.order。例如
svg.selectAll("circle")
.data(data)
.join("circle")
.attr("fill", "none")
.attr("stroke", "black");
enter 函数可以指定为字符串简写,如上所示,它等效于使用给定元素名称的 selection.append。同样,可选的 update 和 exit 函数也可以指定,它们分别默认为恒等函数和调用 selection.remove。因此,上面的简写形式等效于
svg.selectAll("circle")
.data(data)
.join(
enter => enter.append("circle"),
update => update,
exit => exit.remove()
)
.attr("fill", "none")
.attr("stroke", "black");
通过在 enter、update 和 exit 上传递单独的函数,您可以更好地控制发生的事情。并且通过为 selection.data 指定一个键函数,您可以最大限度地减少对 DOM 的更改以优化性能。例如,要为 enter 和 update 设置不同的填充颜色
svg.selectAll("circle")
.data(data)
.join(
enter => enter.append("circle").attr("fill", "green"),
update => update.attr("fill", "blue")
)
.attr("stroke", "black");
由 enter 和 update 函数返回的选择将被合并,然后由 selection.join 返回。
您可以在 enter、update 和 exit 函数内创建过渡来动画化进入、更新和退出。如果 enter 和 update 函数返回过渡,则它们的底层选择将被合并,然后由 selection.join 返回。exit 函数的返回值不会被使用。
有关更多信息,请参见 selection.join 笔记本。
selection.enter()
源代码 · 返回 enter 选择:对于选择中没有相应 DOM 元素的每个数据项,都存在占位符节点。(对于未由 selection.data 返回的选择,enter 选择为空。)
enter 选择通常用于创建与新数据相对应 的“缺少”元素。例如,要从数字数组创建 DIV 元素
const div = d3.select("body")
.selectAll("div")
.data([4, 8, 15, 16, 23, 42])
.enter().append("div")
.text(d => d);
如果主体最初为空,则上面的代码将创建六个新的 DIV 元素,并将它们按顺序追加到主体,并将它们的文本内容分配为关联的(字符串强制转换)数字
<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>
从概念上讲,enter 选择的占位符是指向父级元素的指针(在此示例中,指向文档主体)。enter 选择通常只用作暂时追加元素,并且通常在追加后与 update 选择合并,以便可以对进入和更新的元素应用修改。
selection.exit()
源代码 · 返回 exit 选择:选择中没有找到新数据项的现有 DOM 元素。(对于未由 selection.data 返回的选择,exit 选择为空。)
exit 选择通常用于删除与旧数据相对应 的“多余”元素。例如,要使用新的数字数组更新先前创建的 DIV 元素
div = div.data([1, 2, 4, 8, 16, 32], d => d);
由于指定了一个关键函数(作为恒等函数),并且新数据包含数字 [4, 8, 16],这些数字与文档中现有元素匹配,因此更新选择包含三个 DIV 元素。保留这些元素原样,我们可以使用 enter 选择为 [1, 2, 32] 附加新元素。
div.enter().append("div").text(d => d);
同样,要删除现有的元素 [15, 23, 42]
div.exit().remove();
现在文档主体如下所示
<div>1</div>
<div>2</div>
<div>4</div>
<div>8</div>
<div>16</div>
<div>32</div>
DOM 元素的顺序与数据的顺序匹配,因为旧数据的顺序和新数据的顺序是一致的。如果新数据的顺序不同,请使用 selection.order 对 DOM 中的元素重新排序。有关数据联接的更多信息,请参阅 通用更新模式 笔记本。
selection.datum(value)
源代码 · 获取或设置每个所选元素的绑定数据。与 selection.data 不同,此方法不计算联接,也不影响索引或 enter 和 exit 选择。
如果指定了 value,则将所有所选元素的元素绑定数据设置为指定的值。如果 value 是一个常量,则所有元素都将获得相同的 datum;否则,如果 value 是一个函数,则按顺序为每个所选元素评估该函数,并传入当前 datum (d)、当前索引 (i) 和当前组 (nodes),其中 this 是当前 DOM 元素 (nodes[i])。然后使用该函数设置每个元素的新数据。空值将删除绑定数据。
如果未指定 value,则返回选择中第一个(非空)元素的绑定 datum。这通常仅在您知道选择只包含一个元素时有用。
此方法对于访问 HTML5 自定义数据属性 很有用。例如,给定以下元素
<ul id="list">
<li data-username="shawnbot">Shawn Allen</li>
<li data-username="mbostock">Mike Bostock</li>
</ul>
您可以通过将每个元素的数据设置为内置的 dataset 属性来公开自定义数据属性
selection.datum(function() { return this.dataset; })
selection.merge(other)
源代码 · 返回一个新的选择,将此选择与指定的 other 选择或转换合并。返回的选择具有与此选择相同数量的组和相同的父级。此选择中任何缺失的(空)元素都将使用指定的 selection 中的相应元素(如果存在(非空))填充。(如果 other 选择具有额外的组或父级,则会忽略它们。)
此方法在 selection.join 内部使用,用于在绑定数据后合并 enter 和 update 选择。您也可以显式合并,但请注意,由于合并是基于元素索引的,因此您应该使用保留索引的操作,例如 selection.select 而不是 selection.filter。例如
const odd = selection.select(function(d, i) { return i & 1 ? this : null; ));
const even = selection.select(function(d, i) { return i & 1 ? null : this; ));
const merged = odd.merge(even);
有关更多信息,请参阅 selection.data。
但是,此方法并不适合连接任意选择:如果此选择和指定的 other 选择在同一索引处都有(非空)元素,则此选择的元素将返回到合并中,而 other 选择的元素将被忽略。