d3-selection
selections(选集)允许对文档对象模型(DOM)进行强大的数据驱动转换:设置attributes, styles, properties,,HTML或text内容,等等。使用数据连接的enter和exit选择方法,还可以add或remove与数据对应的元素。
选择方法通常返回当前的选集或新的选集,允许通过链式调用在给定的选集上简洁地应用多个操作。例如,要设置当前文档中所有段落元素的class和颜色样式:
d3.selectAll("p")
.attr("class", "graf")
.style("color", "red");
相当于:
var p = d3.selectAll("p");
p.attr("class", "graf");
p.style("color", "red");
按照惯例,返回当前选集的选择方法使用四个空格进行缩进,而返回新选集的方法仅使用两个空格。这有助于揭示上下文的变化,使它们在链中突出出来:
d3.select("body")
.append("svg")
.attr("width", 960)
.attr("height", 500)
.append("g")
.attr("transform", "translate(20,20)")
.append("rect")
.attr("width", 920)
.attr("height", 460);
选集是不可改变的。影响选定元素(或其顺序)的所有选择方法都返回一个新的选集,而不是修改当前的选集。但是注意,元素必然是可变的,因为选集驱动文档的转换!
Installing
如果你使用npm,请键入npm install d3-selection
。否则,请下载最新版本。你也可以直接从d3js.org加载,作为standalone library(独立库)或D3 4.0的一部分来使用。支持AMD,CommonJS和vanilla环境。在vanilla环境下,会输出一个全局的d3
:
<script src="https://d3js.org/d3-selection.v1.min.js"></script>
<script>
var div = d3.selectAll("div");
</script>
API Reference
- Selecting Elements
- Modifying Elements
- Joining Data
- Handling Events
- Control Flow
- Local Variables
- Namespaces
Selecting Elements
选择方法接受W3C selector strings(W3C选择器字符串),如.fancy
可以选中具有fancy类的元素,div
可以选择DIV元素。选择的方法有两种形式:select和selectAll:前者只选择第一个匹配的元素,而后者按文档顺序选择所有匹配的元素。顶层的选择方法,d3.select和d3.selectall,查询整个文档;下层的选择方法,selection.select和selection.selectall,限制选择选定元素的后代元素。
d3.selection()
选择根元素,document.documentElement
。此函数也可用于检测选集(instanceof d3.selection
)或扩展选集原型。例如,添加一个方法来选中复选框:
d3.selection.prototype.checked = function(value) {
return arguments.length < 1
? this.property("checked")
: this.property("checked", !!value);
};
然后使用:
d3.selectAll("input[type=checkbox]").checked(true);
d3.select(selector)
选择与给定selector字符串匹配的第一个元素。如果没有元素与selector匹配,则返回空选集。如果多个元素与selector匹配,则只选择第一个匹配元素(按文档顺序)。例如,要选择第一个锚点元素:
var anchor = d3.select("a");
如果selector不是字符串,则选择给定的节点;如果你已经有了一个对节点的引用,这是有用的,例如事件监听器中的this
,或全局节点如document.body
。例如,点击段落将其设为红色:
d3.selectAll("p").on("click", function() {
d3.select(this).style("color", "red");
});
d3.selectAll(selector)
选择与给定selector字符串匹配的所有元素。元素将按文档顺序(从上到下)进行选择。如果文档中没有元素与selector匹配,或者selector为null或undefined,则返回空选集。例如,选择所有段落:
var paragraph = d3.selectAll("p");
如果selector不是字符串,则选择给定的节点数组;如果你已经有了一个对节点的引用,这是有用的,例如事件监听器中的this.childNodes
,或全局节点如document.links
。节点是类似于NodeList
或arguments
的pseudo-array(伪数组)。例如,将所有链接都设为红色:
d3.selectAll(document.links).style("color", "red");
selection.select(selector)
对于每个选定元素,选择与给定selector字符串匹配的第一个后代元素。如果当前元素中没有与给定selector匹配的元素,则返回选集中当前索引处的元素为null。(如果selector为null,则返回选集中的每个元素都将为null,从而导致返回空选集。)如果当前元素有关联数据,则此数据会被传入到相应的选定元素。如果多个元素与selector匹配,则只选择文档顺序中的第一个匹配元素。例如,选择每个段落中第一个粗体元素:
var b = d3.selectAll("p").select("b");
如果selector为函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。它必须返回一个元素,如果没有匹配元素则返回null。例如,选择每个段落的前一个兄弟元素:
var previous = d3.selectAll("p").select(function() {
return this.previousElementSibling;
});
不像selection.selectall,selection.select不影响分组:它保持了现有的分组结构和索引,以及选中子元素的传入数据(如果有的话)。分组在数据连接中起着重要的作用。另请参阅Nested Selections和How Selections Work了解更多。
selection.selectAll(selector)
对于每个选定元素,选择与给定selector字符串匹配的后代元素。返回选集中的元素会根据此选集中其对应的父节点进行分组。如果当前元素中没有与给定selector匹配的元素,或selector为null,则当前索引处的组将为空。选定元素不会从此选集中继承数据;可以使用selection.data将数据传入子元素。例如,选择每个段落中的粗体元素:
var b = d3.selectAll("p").selectAll("b");
如果selector为函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。它必须返回一个元素数组(或者说类数组,如NodeList
),或者空数组(如果没有匹配元素)。例如,选择每个段落的前一个和后一个兄弟元素:
var sibling = d3.selectAll("p").selectAll(function() {
return [
this.previousElementSibling,
this.nextElementSibling
];
});
不像selection.select,selection.selectall会影响分组:每个选定的后代都是根据其初始选集中的父元素进行分组的。分组在数据连接中起着重要的作用。另请参阅Nested Selections和How Selections Work了解更多。
selection.filter(filter)
对选集进行筛选,返回一个新的选集,仅包含给定filter下为true的元素。filter可以指定为选择器字符串或函数。如果filter为函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。
例如,筛选表行选集,使其仅包含偶数行:
var even = d3.selectAll("tr").filter(":nth-child(even)");
这大致相当于直接使用d3.selectall,虽然索引可能不同:
var even = d3.selectAll("tr:nth-child(even)");
类似地,使用函数:
var even = d3.selectAll("tr").filter(function(d, i) { return i & 1; });
或者使用selection.select:
var even = d3.selectAll("tr").select(function(d, i) { return i & 1 ? this : null; });
注意::nth-child
伪类的索引从1开始,不是0。另外,上述过滤函数不完全与:nth-child
相同;它们依赖于选集索引,而不是DOM中前面的同级元素的数量。
返回的过滤选集会保留此选集父元素,不像array.filter,它不会保留被移除元素的索引;如果需要的话,使用selection.select保存索引。
selection.merge(other)
返回一个此选集与other选集合并后的新选集。返回的选集具有相同数目的组和与此选集相同的父元素。此选集中的任何缺失(null)元素都将填充指定selection中相应的元素(如果存在(不是null))。(如果other选集有额外的组或父元素,将被忽略)
此方法通常用于在连接数据后合并enter和update选集。在分别修改输入和更新元素之后,可以合并两个选集并执行操作,而无需重复编写代码。例如:
var circle = svg.selectAll("circle").data(data) // UPDATE
.style("fill", "blue");
circle.exit().remove(); // EXIT
circle = circle.enter().append("circle") // ENTER
.style("fill", "green")
.merge(circle) // ENTER + UPDATE
.style("stroke", "black");
另请参阅selection.data了解更多,这被称为general update pattern(一般更新模式)。
然而,此方法不适用于连接任意的选集:如果此选集和other选集在同一索引处都有元素(非null),则将返回此selection中的元素,而other选集中的元素会被忽略。
d3.matcher(selector)
根据给定selector返回一个函数,如果this
元素中与给定选择器匹配,则返回true。selection.filter内部使用此方法。例如:
var div = selection.filter("div");
相当于:
var div = selection.filter(d3.matcher("div"));
虽然D3不是一个compatibility layer(兼容层),但是因为element.matches的最新标准,此实现确实支持vendor-prefixed(浏览器引擎前缀)的实现。
d3.selector(selector)
根据给定selector返回一个函数,返回this
元素中与给定选择器匹配的第一个后代元素。selection.select内部使用此方法。例如:
var div = selection.select("div");
相当于:
var div = selection.select(d3.selector("div"));
d3.selectorAll(selector)
根据给定selector返回一个函数,返回this
元素中与给定选择器匹配的所有后代元素。selection.selectAll内部使用此方法。例如:
var div = selection.selectAll("div");
相当于:
var div = selection.selectAll(d3.selectorAll("div"));
d3.window(node)
返回给定node的所有者窗口。如果node是一个节点,则返回所有者文档的默认视图;如果node是一个文档,则返回它的默认视图,否则返回node。
d3.style(node, name)
根据给定name返回给定node样式属性的值。如果node具有给定name的内联样式,则返回其值;否则返回computed property value(计算属性值)。另请参阅selection.style。
Modifying Elements
选择元素后,使用选集的转换方法来影响文档内容。例如,设置锚点元素的name属性和color样式:
d3.select("a")
.attr("name", "fred")
.style("color", "red");
想要试验selections,访问d3js.org并打开浏览器的开发者控制台!(在Chrome中,使用⌥⌘J打开控制台)选择元素,然后检查返回的选集看哪些元素被选中了以及它们是如何分组的。调用选择方法并查看页面内容是如何改变的。
selection.attr(name[, value])
如果指定了value,则根据给定name将选定元素上的属性设置为给定value,并返回选集。如果value是常量,则所有元素都被赋予相同的属性值;如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后,函数返回值将用于设置每个元素的属性。value为null时将移除指定的属性。
如果不指定value,则返回选集中第一个(非null)元素的指定属性的当前值。这通常只有当你知道选集仅包含一个元素时是有用的。
给定的name可能具有一个命名空间前缀,如在XLink命名空间中xlink:href
用于指定href
属性。另请参阅namespaces查看支持的命名空间的映射;可以通过加入映射来注册其他命名空间。
selection.classed(names[, value])
如果指定了value,则通过设置class
属性或修改classList
属性为选定元素添加或移除给定的CSS类names。给定的names是使用空格分隔的类名字符串。例如,为选定元素添加foo
和bar
类:
selection.classed("foo bar", true);
如果该value为true,则为所有的元素添加给定的class;否则,移除对应class。如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。函数返回值将用于为每个元素添加或移除class。例如,随机地为一半选定元素添加foo
类。
selection.classed("foo", function() { return Math.random() > 0.5; });
如果不指定value,则当且仅当选集中第一个(非null)元素具有指定的class时返回true。这通常只有当你知道选集仅包含一个元素时是有用的。
selection.style(name[, value[, priority]])
如果指定了value,则将选定元素指定的name属性设为指定value并返回此选集。如果value是常量,则所有元素都被赋予相同的属性值;如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后,函数返回值将用于设置每个元素的属性。value为null时将移除指定的属性。也可以指定可选的priority为null或字符串的important
(没有感叹号)。
如果不指定value,则返回选集中第一个(非null)元素指定样式属性的当前值。当前值定义为元素的内联值(如果有的话);否则返回其computed property value(计算属性值)。这通常仅当你知道选集只包含一个元素时访问其当前样式值是有用的。
注意:与许多SVG属性不同,CSS样式通常有关联单位。例如,3px
是合法的stroke-width(绘制宽度)值,而3
不是。一些浏览器隐式地将px
(像素)单位分配给数字值,但并非所有浏览器都这样做:例如,IE会抛出一个“无效参数”错误!
selection.property(name[, value])
有些HTML元素具有特殊属性,这些属性不能使用属性或样式进行访问,例如表单字段的文本value
和复选框的checked
布尔值。可以使用此方法获取或设置这些属性。
如果指定了value,则将选定元素指定的name属性设为指定value并返回此选集。如果value是常量,则所有元素都被赋予相同的属性值;如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后,函数返回值将用于设置每个元素的属性。value为null时将移除指定的属性。
如果不指定value,则返回选集中第一个(非null)元素指定的属性值。这通常只有当你知道选集仅包含一个元素时是有用的。
selection.text([value])
如果指定了value,则将所有选定元素的text content(文本内容)设置为指定值,并替换任何已有子元素。如果value是常量,则所有元素都被赋予相同的文本内容;如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后,函数返回值将用于设置每个元素的文本内容。value为null时将清空内容。
如果不指定value,则返回选集中第一个(非null)元素的文本内容。通常只有当你知道选集仅包含一个元素时是有用的。
selection.html([value])
如果指定了value,则将所有选定元素的inner HTML设置为指定值,并替换任何已有子元素。如果value是常量,则所有元素都被赋予相同的inner HTML;如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后,函数返回值将用于设置每个元素的inner HTML。value为null时将清空内容。
如果不指定value,则返回选集中第一个(非null)元素的inner HTML。通常只有当你知道选集仅包含一个元素时是有用的。
使用selection.append或selection.insert来代替此方法创建数据驱动的内容;此方法的目的是当你需要一点HTML,或者说丰富的格式时使用。同时,selection.html只支持HTML元素。SVG元素和其他非HTML元素不支持innerHTML属性,因此不支持selection.html。可以考虑使用XMLSerializer将DOM子树转换为文本。另请参阅innersvg polyfill,它为SVG元素支持innerHTML属性提供了shim(垫片)。
selection.append(type)
如果type为字符串,则向每个选定元素添加一个此类型(标签名)的新元素作为最后一个子元素,或者如果是enter选集,在update选集中添加到下一个兄弟元素之前。enter选集的后续行为允许你将元素连同数据按序插入DOM;然而,值得注意的是,如果更新元素改变了顺序,则selection.order依然是需要的(即,如果新数据的顺序与原有数据不一致)。
如果value是一个函数,则对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。此函数应该返回要追加的元素(函数通常创建一个新元素,但它可能返回一个现有的元素)。例如,向每个段落追加一个DIV元素:
d3.selectAll("p").append("div");
相当于:
d3.selectAll("p").append(function() {
return document.createElement("div");
});
也相当于:
d3.selectAll("p").select(function() {
return this.appendChild(document.createElement("div"));
});
在这两个例子中,此方法返回包含追加元素的新选集。每一个新的元素继承了当前元素的数据(如果有的话),和selection.select一样。
给定name可能有命名空间前缀,如SVG命名空间中svg:text
指定text
属性,另请参阅namespaces查看支持的命名空间的映射;可以通过加入映射来注册其他命名空间。如果不指定命名空间,则将从父元素继承;或者,如果名称是已知前缀之一,则将使用相应的命名空间(例如,svg
意味着svg:svg
)。
selection.insert(type[, before])
如果给定的type为字符串,则在每个选定元素的第一个与before匹配的元素前插入一个此类型(标签名)的新元素。例如,before为:first-child
时在第一个子元素前添加节点。如果不指定before,默认为null。(想要连同数据按序追加元素,使用selection.append)
可以将type和before指定为对每个选定元素求值的函数,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。type函数应该返回要插入的元素;before函数应该返回需要在何处插入的子元素。例如,向每个段落追加一个DIV元素:
d3.selectAll("p").insert("div");
相当于:
d3.selectAll("p").insert(function() {
return document.createElement("div");
});
也相当于:
d3.selectAll("p").select(function() {
return this.insertBefore(document.createElement("div"), null);
});
在这两个例子中,此方法返回包含追加元素的新选集。每一个新的元素继承了当前元素的数据(如果有的话),和selection.select一样。
给定name可能有命名空间前缀,如SVG命名空间中svg:text
指定text
属性,另请参阅namespaces查看支持的命名空间的映射;可以通过加入映射来注册其他命名空间。如果不指定命名空间,则将从父元素继承;或者,如果名称是已知前缀之一,则将使用相应的命名空间(例如,svg
意味着svg:svg
)。
selection.remove()
从文档中移除选定元素。返回从DOM中分离出来的选集(移除的元素)。目前还没有一个专门的API将删除元素重新添加到文件;然而,你可以通过向selection.append或selection.insert传入一个函数来重新添加元素。
selection.clone([deep])
选择元素后立即插入选定元素的克隆,并返回新添加克隆的选集。如果deep为true,将克隆所选元素的子节点。否则,只克隆元素本身。相当于:
selection.select(function() {
return this.parentNode.insertBefore(this.cloneNode(deep), this.nextSibling);
});
selection.sort(compare)
返回一个新选集,包含此选集中每个分组根据compare函数排序后的副本。排序之后,重新插入元素以匹配结果顺序(每个selection.order)。
compare函数默认为升序,通过将两个元素传入的数据a和b进行比较。它应该返回负值、正值或0。如果为负值,则a应在b之前;如果为正值,则a应在b之后;否则,a和b被认为是相等的,顺序随意。
注意,排序不能保证是稳定的;但是可以保证其行为与浏览器内置的针对数组的sort方法是相同的。
selection.order()
将元素重新插入文档中,使每个组的文档顺序与选集顺序相匹配。相当于对已排序的数据调用selection.sort,但快得多。
selection.raise()
依次将每个选定元素重新插入,作为其父元素的最后一个子元素。相当于:
selection.each(function() {
this.parentNode.appendChild(this);
});
selection.lower()
依次将每个选定元素重新插入,作为其父元素的第一个子元素。相当于:
selection.each(function() {
this.parentNode.insertBefore(this, this.parentNode.firstChild);
});
d3.create(name)
根据给定元素name,返回一个仅有一个元素的选集,包含根据给定name从文档中分离出来的元素。
d3.creator(name)
根据给定元素name,返回一个可以根据给定name创建元素的函数,假设this
是父元素。selection.append和selection.insert内部使用此方法创建新元素。例如:
selection.append("div");
相当于:
selection.append(d3.creator("div"));
另请参阅namespaces查看支持的命名空间的映射,如针对SVG元素的。
Joining Data
有关D3数据连接的介绍,请参阅Thinking With Joins。另请参阅General Update Pattern查看示例。
selection.data([data[, key]])
将给定的data数组与选定的元素连接起来,返回一个表示update选集的新选集:成功绑定到数据的元素。同时在返回选集上定义了enter和exit选集,可以用来添加或移除元素以适应新数据。给定的data是包含任意值(例如,数字或对象)的数组,或返回包含每个分组值的数组的函数。当数据被分配到一个元素,它将存储在属性__data__
中,从而使数据具有“粘性”以适应再次选择。
为选集中的每个组指定data。如果选集有多个组(如在d3.selectAll后使用selection.selectAll),则data通常应该指定为一个函数。该函数将按顺序对每个组求值,传入该组父元素的数据(d,可能为undefined),组索引(i)和选集的父节点(nodes),并将this作为分组的父元素。例如,根据一个数字矩阵创建一个HTML表格:
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var tr = d3.select("body")
.append("table")
.selectAll("tr")
.data(matrix)
.enter().append("tr");
var td = tr.selectAll("td")
.data(function(d) { return d; })
.enter().append("td")
.text(function(d) { return d; });
在这个例子中,data函数是一个恒等函数:对于每个表行,它从数据矩阵返回相应的行。
如果不指定key函数,那么data中的第一个数据会被分配给第一个选定的元素,第二个数据分配到第二个选定的元素,以此类推。可以指定一个key函数代替默认以索引进行连接的函数,通过为每个数据和元素计算一个字符串标识符来控制将哪个数据分配给哪个元素。key函数会对每个选定元素求值,依次传入当前数据(d),当前索引(i)和当前组(nodes),并将this作为DOM元素(nodes[i]),返回的字符串为数据的键。然后,key函数也会对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>
可以通过键连接数据:
var data = [
{name: "Locke", number: 4},
{name: "Reyes", number: 8},
{name: "Ford", number: 15},
{name: "Jarrah", number: 16},
{name: "Shephard", number: 31},
{name: "Kwon", number: 34}
];
d3.selectAll("div")
.data(data, function(d) { return d ? d.name : this.id; })
.text(function(d) { return d.number; });
在这个例子中,如果数据d存在,则key函数使用此数据d,否则使用元素的id属性。由于这些元素之前没有绑定到数据,所以当key函数对选定元素求值时数据d为null,当key函数对新数据求值时数据d是非null的。
update和enter选集按数据顺序返回,而exit选集则保持着连接之前的选集顺序。如果指定了key函数,则选集中的元素顺序可能与其在文档中的顺序不一致;必要时可以使用selection.order或selection.sort。有关key函数如何影响连接的更多信息,另请参阅A Bar Chart, Part 2和Object Constancy。
虽然数据连接可以简单地用来创建(enter)与数据相对应的一组元素,但更广泛地说,数据连接的设计是为了能让你根据需要创建、销毁或更新元素,以便生成的DOM与新数据相对应。数据连接可以通过只在元素的每个状态(entering、updating或exiting)执行最小的必要操作来高效地实现这一点,并允许在状态之间声明简洁的动画转换。下面是General Update Pattern的简单示例:
var circle = svg.selectAll("circle") // 1
.data(data) // 2
.style("fill", "blue"); // 3
circle.exit().remove(); // 4
circle = circle.enter().append("circle") // 5, 9
.style("fill", "green") // 6
.merge(circle) // 7
.style("stroke", "black"); // 8
分离成几个步骤:
- 选中所有的circle(都是
svg
选集的后代)。 - 将这些circle连接到新的data,返回匹配的circle:update选集。
- 将这些updating circle填充为blue。
- 移除所有与新数据不匹配的circle(exit选集)。
- 为与已有circle不匹配的新数据追加新circle:enter选集。
- 将这些entering circle填充为green。
- 创建一个新的选择,表示entering和updating circle的并集。
- 为这些entering和updating circle绘制black边线。
- 将这些circle存储在变量
circle
中。
如上文所述,“匹配”逻辑是由key函数通过传入的selection.data确定的;因为上述代码示例中没有使用key函数,所以元素和数据通过索引进行匹配。
如果不指定data,则此方法将返回包含所选元素的数据数组。
此方法不能用于清除绑定数据;需要清除数据时请使用selection.datum。
selection.enter()
返回enter选集:每个在选集中没有对应DOM元素的数据的placeholder nodes(占位符节点)。(对不是由selection.data返回的选集来说enter选集为空)
enter选集通常用于创建与新数据相对应的“缺失”元素。例如,要从数字数组中创建DIV元素:
var div = d3.select("body")
.selectAll("div")
.data([4, 8, 15, 16, 23, 42])
.enter().append("div")
.text(function(d) { return d; });
如果body初始为空,上面的代码将创建六个新的DIV元素,按顺序将它们追加到body中,并将它们的文本内容指定为关联的(强制转换为字符串)数字:
<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>
从概念上讲,enter选集的占位符是父元素的指针(在这个例子中为body)。enter选集通常只用于临时追加元素,并且经常在追加后与update选集合并,以便能同时对entering和updating元素应用修改。
selection.exit()
返回exit选集:选集中没有新数据的DOM元素。(对不是由selection.data返回的选集来说exit选集为空)
exit选集通常用于删除与旧数据相对应的“superfluous(多余)”元素。例如,根据一个新的数据数组更新之前创建的DIV元素:
div = div.data([1, 2, 4, 8, 16, 32], function(d) { return d; });
由于指定了一个key函数(作为恒等函数),并且新数据包含与文档中现有元素匹配的数字[ 4, 8, 16 ],所以update选集包含三个DIV元素。为保留这些元素,可以使用enter选集为[ 1, 2, 32 ]添加新元素:
div.enter().append("div").text(function(d) { return d; });
同样,要删除exiting元素[ 15, 23, 42 ]:
div.exit().remove();
现在body看起来是这样的:
<div>1</div>
<div>2</div>
<div>4</div>
<div>8</div>
<div>16</div>
<div>32</div>
DOM元素的顺序之所以与数据的顺序相匹配,是因为旧数据的顺序和新数据的顺序是一致的。如果新数据的顺序不同,可以使用selection.order在DOM种对元素重新排序。另请参阅General Update Pattern了解更多。
selection.datum([value])
获取或设置每个选定元素的绑定数据。与selection.data不同的是,此方法不计算连接,并且不会影响索引或enter和exit选集。
如果指定了value,则将所有选定元素的绑定数据设为value。如果value为常量,则所有元素设置为相同的数据;如果value为函数,则将对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。然后该函数将用于设置每个元素的新数据。value为null时将删除绑定数据。
如果不指定value,则返回选集中第一个(非null)元素的绑定数据。通常只有当你知道选集仅包含一个元素时是有用的。
此方法对于访问HTML5自定义data属性非常有用。例如,给定以下元素:
<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; })
Handling Events
对于交互,选集允许监听和派发事件。
selection.on(typenames[, listener[, capture]])
根据给定事件typenames为每个选定元素添加或移除listener。typenames为一个字符串形式的事件类型,如click
,mouseover或submit
;任何你浏览器支持的DOM event type(DOM事件类型)都可以使用。类型后面可以选择跟上一个句点(.
)和名字;可选的名字允许注册多个回调函数接收同一类型的事件,如click.foo
和click.bar
。想要同时指定多个typenames,可以使用空格分开,如input change
或click.foo click.bar
。
当选定元素上的特定事件派发后,给定的listener将对元素求值,传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。监听器始终监听到其元素的最新数据,但索引是选集的属性,在监听器被分配时是固定的;想要更新索引,可以重新分配监听器。想要访问监听器内的当前事件,请使用d3.event。
如果选定元素上已经注册了同typename的事件监听器,则会在添加新监听器前移除原有监听器。想要移除监听器,传入null作为listener。想要根据给定名字移除所有监听器,传入null作为listener,并传入.foo
作为typename,foo
即为名字;想要移除所有没有名字的监听器,将.
指定为typename。
可以指定一个符合W3C useCapture flag的capture标识:“开启捕获后,所有特定类型的事件会在派发给已注册的EventListener前派发给其下面的所有EventTarget。通过冒泡产生的事件不会触发指定为使用捕获模式的EventListener。”
如果不指定listener,则返回选集中第一个(非null)元素上特定事件typename当前分配的事件。如果指定了多个typenames,则返回第一个匹配的监听器。
selection.dispatch(type[, parameters])
按顺序将指定type的自定义事件派发给每个选定的元素。可以指定一个额外的parameters映射以设置事件的附加属性。它可能包含以下字段:
- bubbles - 如果为true,事件将向上派发给祖先。
- cancelable - 如果为true,则允许使用event.preventDefault。
- detail - 任何与事件相关联的自定义数据。
如果parameters为函数,则将对每个选定元素求值,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。它必须返回当前元素的参数映射。
d3.event
当前事件,如果有的话。这是在事件监听器的调用过程中设置的,并在监听器终止后重新设置。使用这个来访问标准事件字段(如event.timeStamp) 方法(如event.preventDefault)。虽然你可以使用原生的event.pageX和event.pageY,但是使用 d3.mouse, d3.touch或d3.touches将事件位置转换为容器的局部坐标系统更方便。
如果你使用了Babel,Webpack或其他ES6转ES5的打包工具,注意,d3.event的值在事件中会变化!引入d3.event必须是live binding(实时打包),所以你可能需要配置打包工具从D3’s ES6模块引入而不是生成的UMD bundle;不是所有打包工具都遵守jsnext:main。同时要注意与window.event冲突。
d3.customEvent(event, listener[, that[, arguments]])
调用给定的listener,使用给定的that this
上下文并传递给定的arguments,如果有的话。在调用期间,d3.event将被设置为给定的event;监听器返回后(或抛出一个错误),d3.event恢复到其之前的值。此外,将event.sourceEvent设为d3.event的先前值,允许自定义事件保持对原始事件的引用。返回由listener返回的值。
d3.mouse(container)
返回当前事件相对于指定container的x和y坐标。容器可以是HTML或SVG容器元素,如g元素或SVG元素。返回的坐标为二元数字数组[x,y ]。
d3.touch(container[, touches], identifier)
返回与当前事件相关联的指定identifier触摸时相对于指定container的x和y坐标。容器可以是HTML或SVG容器元素,如g元素或SVG元素。返回的坐标为二元数字数组[x,y ]。如果touches中没有带有指定identifier的触摸,则返回null;这对于忽略touchmove事件是有用的。如果不指定touches,默认为当前事件的changedTouches属性。
d3.touches(container[, touches])
返回与当前事件相关联的触摸相对于指定container的x和y坐标。容器可以是HTML或SVG容器元素,如g元素或SVG元素。返回的坐标为拥有二元数字数组的数组[[x1, y1], [x2, y2], …]。如果不指定touches,默认为当前事件的touches属性。
d3.clientPoint(container, event)
返回指定event相对于指定container的x和y坐标(该event也可能是touch)。容器可以是HTML或SVG容器元素,如g元素或SVG元素。返回的坐标为二元数字数组[x,y ]。
Control Flow
对于高级用法,selections提供了自定义控件流的方法。
selection.each(function)
为每个选定元素调用指定的function,依次传入当前数据(d)、当前索引(i)和当前组(nodes),并将this作为当前DOM元素(nodes[i])。此方法可用于为每个选定的元素调用任意代码,并有助于创建一个能同时访问父子数据的上下文,如:
parent.each(function(p, j) {
d3.select(this)
.selectAll(".child")
.text(function(d, i) { return "child " + d.name + " of " + p.name; });
});
另请参阅Sized Donut Multiples查看示例。
selection.call(function[, arguments…])
调用给定function一次,传入可选的arguments。返回此选集。这相当于手动调用函数,但有助于链式调用。例如,在可重用函数中设置多个样式:
function name(selection, first, last) {
selection
.attr("first-name", first)
.attr("last-name", last);
}
现在可以这样写:
d3.selectAll("div").call(name, "John", "Snow");
这大致相当于:
name(d3.selectAll("div"), "John", "Snow");
唯一不同的是,selection.call总是返回selection而不是被调用function的返回值,name
。
selection.empty()
如果此选集中没有(非null)元素则返回true。
selection.nodes()
返回一个包含此选集中所有(非null)元素的数组。
selection.node()
返回此选集中第一个(非null)元素。如果选集为空,则返回null。
selection.size()
返回此选集中的元素总数。
Local Variables
D3 locals允许你定义与数据无关的本地状态。例如,当渲染时间序列数据的small multiples时,你可能想要所有图表的x-scale相同而y-scales不同,以比较每个度量的相对表现。D3 locals的作用由DOM元素决定,在set时,值存储在给定的元素中;在get时,值会从给定元素或定义其的最近祖先上获取。
d3.local()
声明一个新的本地变量。例如:
var foo = d3.local();
与var
相似的是,每个变量都是一个不同的引用符号;与var
不同的是,每个变量的值的作用域也受限于DOM。
local.set(node, value)
设置指定node的local值为value,并返回给定value。这通常表现为使用selection.each:
selection.each(function(d) { foo.set(this, d.value); });
如果你只是设置一个变量,考虑使用selection.property:
selection.property(foo, function(d) { return d.value; });
local.get(node)
返回指定node的local值。如果node没有定义此local,则返回定义它的最近祖先的值。如果没有祖先定义此local则返回undefined。
local.remove(node)
删除给定node中此local的值。如果在删除前node定义了此local则返回true,否则返回false。如果祖先上也定义了此local,这些定义不会受影响,因此,local.get依然可以返回继承的值。
local.toString()
返回此local自动生成的标识符。这是元素用来存储local值的属性名,因此你也可以使用element[local]或selection.property来设置或获取local的值。
Namespaces
XML命名空间很有趣!对吗?幸运的是,你几乎可以忽略它们。
d3.namespace(name)
限定给定的可能有命名空间前缀的name。如果名称包含一个冒号(:
),则冒号前面的字符串被认为是命名空间前缀,必须在d3.namespaces中注册。返回一个对象,其space
和local
属性描述了命名空间URL的全名和local名。例如:
d3.namespace("svg:text"); // {space: "http://www.w3.org/2000/svg", local: "text"}
如果名称不包含冒号,则此函数只返回输入名称。
d3.namespaces
已注册的命名空间前缀的映射。初始值为:
{
svg: "http://www.w3.org/2000/svg",
xhtml: "http://www.w3.org/1999/xhtml",
xlink: "http://www.w3.org/1999/xlink",
xml: "http://www.w3.org/XML/1998/namespace",
xmlns: "http://www.w3.org/2000/xmlns/"
}
附加的前缀可以根据需要在其他名称空间中创建元素或属性。