Skip to content

连接数据

¥Joining data

有关简介,请参阅 连接思考selection.join 注意本

¥For an introduction, see Thinking With Joins and the selection.join notebook.

selection.data(data, key) {#selection_data}

源代码 · 将指定的数据数组与选定元素绑定,返回一个表示更新选择的新选择:成功绑定到数据的元素。还定义了返回选择集的 enterexit 选择集,可用于添加或删除元素以对应新数据。指定的数据是一个任意值(例如,数字或对象)的数组,或是一个返回每个组的值数组的函数。将数据赋值给元素后,数据将存储在属性 __data__ 中,从而使数据具有“粘性”,并在重新选择时可用。

¥Source · Binds the specified array of data with the selected elements, returning a new selection that represents the update selection: the elements successfully bound to data. Also defines the enter and exit selections on the returned selection, which can be used to add or remove elements to correspond to the new data. The specified data is an array of arbitrary values (e.g., numbers or objects), or a function that returns an array of values for each group. When data is assigned to an element, it is stored in the property __data__, thus making the data “sticky” and available on re-selection.

数据是为选择集中的每个组指定的。如果选择包含多个组(例如 d3.selectAll 后跟 selection.selectAll),则通常应将数据指定为函数。此函数将按顺序为每个组进行评估,传递组的父数据(d,可能未定义)、组索引 (i) 以及选择的父节点(nodes),并将 this 作为组的父元素。

¥The data is specified for each group in the selection. If the selection has multiple groups (such as d3.selectAll followed by selection.selectAll), then data should typically be specified as a function. This function will be evaluated for each group in order, being passed the group’s parent datum (d, which may be undefined), the group index (i), and the selection’s parent nodes (nodes), with this as the group’s parent element.

selection.join(或更明确地与 selection.enterselection.exitselection.appendselection.remove)结合使用时,selection.data 可用于输入、更新和退出元素以匹配数据。例如例如,要从数字矩阵创建 HTML 表格:

¥In conjunction with selection.join (or more explicitly with selection.enter, selection.exit, selection.append and selection.remove), selection.data can be used to enter, update and exit elements to match data. For example, to create an HTML table from a matrix of numbers:

js
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);

在此示例中,数据函数是恒等函数:对于表格的每一行,它返回数据矩阵中对应的行。

¥In this example the data function is the identity function: for each table row, it returns the corresponding row from the data matrix.

如果未指定关键函数,则将数据中的第一个数据分配给第一个选定元素,将第二个数据分配给第二个选定元素,依此类推。可以指定一个关键函数来控制将哪个数据分配给哪个元素,通过为每个数据和元素计算字符串标识符来取代默认的按索引连接。此关键函数按顺序为每个选定元素求值,传递当前数据 (d)、当前索引 (i) 和当前组 (nodes),并将其作为当前 DOM 元素 (nodes[i]);返回的字符串是元素的键。然后,key 函数也会针对数据中的每个新数据进行求值,传入当前数据 (d)、当前索引 (i) 以及组的新数据,并将其作为组的父 DOM 元素;返回的字符串是数据的键。给定键的数据将分配给具有匹配键的元素。如果多个元素具有相同的键,则重复的元素将放入退出选择器中;如果多个数据具有相同的键,则重复的数据将放入输入选择中。

¥If a key function is not specified, then the first datum in data is assigned to the first selected element, the second datum to the second selected element, and so on. A key function may be specified to control which datum is assigned to which element, replacing the default join-by-index, by computing a string identifier for each datum and element. This key function is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]); the returned string is the element’s key. The key function is then also evaluated for each new datum in data, being passed the current datum (d), the current index (i), and the group’s new data, with this as the group’s parent DOM element; the returned string is the datum’s key. The datum for a given key is assigned to the element with the matching key. If multiple elements have the same key, the duplicate elements are put into the exit selection; if multiple data have the same key, the duplicate data are put into the enter selection.

例如,给定以下文档:

¥For example, given this document:

html
<div id="Ford"></div>
<div id="Jarrah"></div>
<div id="Kwon"></div>
<div id="Locke"></div>
<div id="Reyes"></div>
<div id="Shephard"></div>

你可以按如下方式按键连接数据:

¥You could join data by key as follows:

js
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);

此示例的 key 函数使用数据 d(如果存在),否则将回退到元素的 id 属性。由于这些元素之前未绑定到数据,因此在选定元素上评估关键函数时,数据 d 为空;在新数据上评估关键函数时,数据 d 为非空。

¥This example key function uses the datum d if present, and otherwise falls back to the element’s id property. Since these elements were not previously bound to data, the datum d is null when the key function is evaluated on selected elements, and non-null when the key function is evaluated on the new data.

更新和输入选择按数据顺序返回,而退出选择保留连接之前的选择顺序。如果指定了关键函数,则选择中的元素顺序可能与文档中的顺序不一致;根据需要使用 selection.orderselection.sort。有关 key 函数如何影响连接的更多信息,请参阅 条形图,第二部分对象恒常性

¥The update and enter selections are returned in data order, while the exit selection preserves the selection order prior to the join. If a key function is specified, the order of elements in the selection may not match their order in the document; use selection.order or selection.sort as needed. For more on how the key function affects the join, see A Bar Chart, Part 2 and Object Constancy.

如果未指定 data,则此方法返回所选元素的数据数组。

¥If data is not specified, this method returns the array of data for the selected elements.

此方法不能用于清除绑定数据;改用 selection.datum

¥This method cannot be used to clear bound data; use selection.datum instead.

selection.join(enter, update, exit) {#selection_join}

源代码 · 根据需要附加、移除和重新排序元素,以匹配先前由 selection.data 绑定的数据,并返回 merged 的输入和更新选择。此方法是显式 一般更新模式 的便捷替代方案,可替换 selection.enterselection.exitselection.appendselection.removeselection.order。例如:

¥Source · Appends, removes and reorders elements as necessary to match the data that was previously bound by selection.data, returning the merged enter and update selection. This method is a convenient alternative to the explicit general update pattern, replacing selection.enter, selection.exit, selection.append, selection.remove, and selection.order. For example:

js
svg.selectAll("circle")
  .data(data)
  .join("circle")
    .attr("fill", "none")
    .attr("stroke", "black");

enter 函数可以像上面一样以字符串简写形式指定,相当于使用给定元素名称的 selection.append。同样,可以指定可选的更新函数和退出函数,它们分别默认为身份函数和调用 selection.remove。因此,上述简写等效于:

¥The enter function may be specified as a string shorthand, as above, which is equivalent to selection.append with the given element name. Likewise, optional update and exit functions may be specified, which default to the identity function and calling selection.remove, respectively. The shorthand above is thus equivalent to:

js
svg.selectAll("circle")
  .data(data)
  .join(
    enter => enter.append("circle"),
    update => update,
    exit => exit.remove()
  )
    .attr("fill", "none")
    .attr("stroke", "black");

通过在进入、更新和退出时传递单独的函数,你可以更好地控制发生的事件。通过为 selection.data 指定一个关键函数,你可以最大限度地减少对 DOM 的更改,从而优化性能。例如,为输入和更新设置不同的填充颜色:

¥By passing separate functions on enter, update and exit, you have greater control over what happens. And by specifying a key function to selection.data, you can minimize changes to the DOM to optimize performance. For example, to set different fill colors for enter and update:

js
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 返回。

¥The selections returned by the enter and update functions are merged and then returned by selection.join.

你可以通过在 enter、update 和 exit 函数中创建过渡效果来为 enter、update 和 exit 动画。如果 enter 和 update 函数返回过渡,则它们的底层选择将被合并,然后由 selection.join 返回。exit 函数的返回值未使用。

¥You can animate enter, update and exit by creating transitions inside the enter, update and exit functions. If the enter and update functions return transitions, their underlying selections are merged and then returned by selection.join. The return value of the exit function is not used.

更多信息,请参阅 selection.join 注意本

¥For more, see the selection.join notebook.

selection.enter() {#selection_enter}

源代码 · 返回进入选择:为选择中没有对应 DOM 元素的每个数据添加占位符节点。(对于 selection.data 未返回的选择,输入选择为空。)

¥Source · Returns the enter selection: placeholder nodes for each datum that had no corresponding DOM element in the selection. (The enter selection is empty for selections not returned by selection.data.)

enter 选择器通常用于创建与新数据对应的“缺失”元素。例如,要从数字数组创建 DIV 元素:

¥The enter selection is typically used to create “missing” elements corresponding to new data. For example, to create DIV elements from an array of numbers:

js
const div = d3.select("body")
  .selectAll("div")
  .data([4, 8, 15, 16, 23, 42])
  .enter().append("div")
    .text(d => d);

如果主体最初为空,则上述代码将创建六个新的 DIV 元素,按顺序将它们附加到主体,并将其文本内容赋值为关联的(字符串强制转换的)数字:

¥If the body is initially empty, the above code will create six new DIV elements, append them to the body in-order, and assign their text content as the associated (string-coerced) number:

html
<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>

从概念上讲,输入选择的占位符是指向父元素(在本例中为文档主体)的指针。enter 选择器通常仅临时用于附加元素,并且通常在附加后使用 merged 和更新选择器,以便可以对输入和更新元素进行修改。

¥Conceptually, the enter selection’s placeholders are pointers to the parent element (in this example, the document body). The enter selection is typically only used transiently to append elements, and is often merged with the update selection after appending, such that modifications can be applied to both entering and updating elements.

selection.exit() {#selection_exit}

源代码 · 返回退出选择:选择集中现有的 DOM 元素,但未找到新的数据。(对于 selection.data 未返回的选择,退出选择为空。)

¥Source · Returns the exit selection: existing DOM elements in the selection for which no new datum was found. (The exit selection is empty for selections not returned by selection.data.)

exit 选择器通常用于删除与旧数据对应的“多余”元素。例如,要用新的数字数组更新先前创建的 DIV 元素:

¥The exit selection is typically used to remove “superfluous” elements corresponding to old data. For example, to update the DIV elements created previously with a new array of numbers:

js
div = div.data([1, 2, 4, 8, 16, 32], d => d);

由于指定了关键函数(作为恒等函数),并且新数据包含与文档中现有元素匹配的数字 [4, 8, 16],因此更新选择包含三个 DIV 元素。保留这些元素的原样,我们可以使用 Enter 选择为 [1, 2, 32] 添加新元素:

¥Since a key function was specified (as the identity function), and the new data contains the numbers [4, 8, 16] which match existing elements in the document, the update selection contains three DIV elements. Leaving those elements as-is, we can append new elements for [1, 2, 32] using the enter selection:

js
div.enter().append("div").text(d => d);

同样,要删除现有元素 [15, 23, 42]:

¥Likewise, to remove the exiting elements [15, 23, 42]:

js
div.exit().remove();

现在文档主体如下所示:

¥Now the document body looks like this:

html
<div>1</div>
<div>2</div>
<div>4</div>
<div>8</div>
<div>16</div>
<div>32</div>

DOM 元素的顺序与数据的顺序一致,因为旧数据的顺序和新数据的顺序是一致的。如果新数据的顺序不同,则使用 selection.order 对 DOM 中的元素进行重新排序。有关数据连接的更多信息,请参阅 一般更新模式 注意本。

¥The order of the DOM elements matches the order of the data because the old data’s order and the new data’s order were consistent. If the new data’s order is different, use selection.order to reorder the elements in the DOM. See the general update pattern notebook for more on data joins.

selection.datum(value) {#selection_datum}

源代码 · 获取或设置每个选定元素的绑定数据。Unlike selection.data, this method does not compute a join and does not affect indexes or the enter and exit selections.

¥Source · Gets or sets the bound data for each selected element. Unlike selection.data, this method does not compute a join and does not affect indexes or the enter and exit selections.

如果指定了值,则在所有选定元素上将元素的绑定数据设置为指定值。如果值是常量,则所有元素都将赋予相同的数据;否则,如果该值是一个函数,则按顺序为每个选定元素求值,并传递当前数据 (d)、当前索引 (i) 和当前组 (nodes),并将其作为当前 DOM 元素 (nodes[i])。然后使用该函数设置每个元素的新数据。空值将删除绑定的数据。

¥If a value is specified, sets the element’s bound data to the specified value on all selected elements. If the value is a constant, all elements are given the same datum; otherwise, if the value is a function, it is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]). The function is then used to set each element’s new data. A null value will delete the bound data.

如果未指定值,则返回选择项中第一个(非空)元素的绑定数据。这通常仅在你知道选择内容仅包含一个元素时才有用。

¥If a value is not specified, returns the bound datum for the first (non-null) element in the selection. This is generally useful only if you know the selection contains exactly one element.

此方法对于访问 HTML5 自定义数据属性 很有用。例如,给定以下元素:

¥This method is useful for accessing HTML5 custom data attributes. For example, given the following elements:

html
<ul id="list">
  <li data-username="shawnbot">Shawn Allen</li>
  <li data-username="mbostock">Mike Bostock</li>
</ul>

你可以通过将每个元素的数据设置为内置的 dataset 属性来公开自定义数据属性:

¥You can expose the custom data attributes by setting each element’s data as the built-in dataset property:

js
selection.datum(function() { return this.dataset; })

selection.merge(other) {#selection_merge}

源代码 · 返回一个新的选择集,将当前选择集与指定的其他选择集或过渡合并。返回的选择节点与此选择节点具有相同数量的组和相同的父节点。此选择集中任何缺失(空)元素都将使用指定选择集中的相应元素(如果存在,则为非空)填充。(如果其他选择项有其他组或父级,则会忽略它们。)

¥Source · Returns a new selection merging this selection with the specified other selection or transition. The returned selection has the same number of groups and the same parents as this selection. Any missing (null) elements in this selection are filled with the corresponding element, if present (not null), from the specified selection. (If the other selection has additional groups or parents, they are ignored.)

此方法由 selection.join 内部使用,用于在绑定数据后合并 enter​​ 和 update 的选择。你也可以显式合并,但请注意,由于合并基于元素索引,因此你应该使用保留索引的操作,例如使用 selection.select 而不是 selection.filter。例如:

¥This method is used internally by selection.join to merge the enter and update selections after binding data. You can also merge explicitly, although note that since merging is based on element index, you should use operations that preserve index, such as selection.select instead of selection.filter. For example:

js
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

¥See selection.data for more.

但是,此方法不适用于连接任意选择:如果此选择项和指定的另一个选择项在同一索引处都有(非空)元素,则合并时返回此选择的元素,并忽略另一个选择的元素。

¥This method is not intended for concatenating arbitrary selections, however: if both this selection and the specified other selection have (non-null) elements at the same index, this selection’s element is returned in the merge and the other selection’s element is ignored.