选择器与数据绑定 — D3 的心脏¶
这一章讲什么: D3 和其他 DOM 库的分水岭是数据绑定:不是手动增删元素,而是把数据数组绑到一批元素上, 让数据自己决定哪些元素该出现、该消失、该更新。 本章把这条机制的每一个零件拆开:选择器对象内部长什么样、
data()之后元素和数据怎么对账、join()替你做了什么、以及数据顺序变了该怎么办。 原书的说法不夸张:本章是「掌握 D3 的核心」1——读懂它,任何 D3 代码都拦不住你。
1. 顶层全景:一次数据更新的完整对账流程¶
已有元素(页面上 5 个圆) 新数据数组(3 个值)
│ │
└──────── selectAll("circle") ───┘
│ data([19,15,26])
▼
┌─── 对账(three-way)───┐
│ _groups → 对得上的(更新) │
│ _enter → 数据多出 │ → join() 新建元素补上
│ _exit → 元素多出 │ → join() 删除元素
└─────────────────────────┘
│ join("circle")
▼
合并后的选择(每个元素都绑着新数据)
│ attr / style / transition…
▼
画面更新
图说:join 的核心是「三方对账」——数据与元素按默认规则(数组下标)配对,
多退少补,最后合并成一个全体可用的选择。
本章的主线例子就是这张流程图本身:一个空 SVG、一组圆、三次数组变化, 我们在浏览器控制台里一步步观察选择器对象的内部数组如何变化—— 每个数都是真实运行结果(原书在控制台里逐段演示)2。
2. 选择器对象:先看清你手里拿的是什么¶
2.1 全局选择函数¶
一切从顶层两个函数开始(第 01 章已经用过)3:
| 函数 | 返回 |
|---|---|
d3.select(选择器或节点) |
选中第一个匹配元素的选择器(单元素) |
d3.selectAll(选择器或节点列表) |
选中全部匹配元素的选择器(多元素) |
关键性质:即使一个元素都没匹配到,也照样返回选择器对象。
d3.selectAll("div.klm") 找不到任何元素,返回的选择器里装着一个空列表——
但它是合法对象,绑上数据之后就能往里造元素4。这是「先选空的、再用 join 创建」这招能成立的原因。
选择器对象有两个内部属性,值得在控制台里展开看一眼:
_groups:选中的元素,按组存放;_parents:当前的分组上下文(「这些元素是谁的孩子」)。
它们不是给你在代码里直接用的(原书专门加了「重要提示」:内部属性不属于公开 API, 调试时可以看,代码里只许用方法访问)5,但看懂它们,后面 join 的行为就不再是黑箱。
从节点反着走:事件处理函数里拿到的是原生 DOM 节点,d3.select(node) 把它包回选择器,
就能继续用 D3 方法;反向操作是 .nodes()(返回节点数组)和 .node()(返回第一个节点)——
比如选中 <canvas> 后要调原生方法 getContext("2d") 画图,就得先 .node() 取出真身6。
2.2 嵌套选择:select 与 selectAll 的分水岭¶
在已有选择器上继续选择,有两个方法,差别致命7:
| 方法 | 数据 | 分组(_parents) |
|---|---|---|
selection.select("p") |
继承父选择器的数据 | 不保留(父是文档根) |
selection.selectAll("p") |
不继承(子元素用自己的数据) | 保留(父是各分组元素) |
后果:往 selectAll 的结果里 append,新元素会追加到对应父元素下;
往 select 的结果里 append,新元素可能挂到 <html> 底下。
原书给的 console 实验:d3.select("div").selectAll("p:first-child") 和
d3.select("div").select("p") 选的是同一个 <p>,但只有前者的 _parents 是 div8。
「数据继承」正是第 03 章柱状图更新能写成 entries.select(".bar") 的原因:
组上绑着数据,组里的柱子是 selectAll 进来的子元素——按上表,selectAll 不传播数据……
那柱状图里子元素的 d 是哪来的?答案是那个场景的 select 路径(单元素选择传播数据),
以及子元素在创建时由 append 继承了当时父级的数据。这条继承链是 D3 里最容易绕晕、
也最常被面试问到的细节,原书用两张控制台截图把两种 _parents 摆在一起对比9。
2.3 读写元素:attr / style / text / classed / property¶
五个最常用的读写方法,规则统一:只传名字是读(返回第一个元素的值),带值是写(返回选择器本身,可链式)10:
| 方法 | 管什么 | 例子 |
|---|---|---|
attr(name, value) |
标签属性(x、r、transform) | .attr("r", 40) |
style(name, value) |
CSS 样式(fill、font-size) | .style("fill", "green") |
text(value) |
文本内容 | .text(d => d) |
classed(names, bool) |
加减类名(只写不读) | .classed("even", d => d%2==0) |
property(name, bool) |
DOM 属性(disabled、checked、hidden) | .property("hidden", d => d<50) |
attr 和 style 的分工在 SVG 里有一个实际差别:attr("stroke", …) 生成 XML 属性,
style("stroke", …) 生成 style 属性里的 CSS——而 CSS 的优先级高于 XML 属性,
样式表里一句 circle { stroke: blue } 会把 attr 写的黑边盖成蓝的。
所以原书建议:fill、stroke、字体这类,用 style();静态样式多的时候,干脆写样式表,
动态逐个变的才用回调 + style11。例外:transform 用 CSS 写要带单位(translate(25px,25px)),
用 XML 属性写不用(translate(25,25))——transform 建议走 attr12。
2.4 回调函数的三个参数¶
所有写方法的值都可以是回调,回调按元素逐个执行,最多收三个参数13:
(d, i, nodes) =>
d —— 当前元素绑的数据(datum);没绑数据时是 undefined
i —— 当前元素在组内的下标(index)
nodes —— 包含当前元素的节点数组;当前元素 = nodes[i]
图说:d、i、nodes 三个参数,加上普通函数里的 this(也指向当前元素)。
两个坑原书专门点过:
- 箭头函数没有自己的
this。想要当前元素,要么用普通函数(用this), 要么用nodes[i]:(d,i,n) => n[i]14。 classed和property配合布尔回调是交互利器:数据更新后类名自动加减, CSS 跟着变(例:偶数染黄底),property("hidden", d => d < 50)按数据隐藏元素15。
2.5 filter:压缩选择器,不保序 vs 保序¶
filter(回调) 返回只含「回调返回 true」的元素的新选择器。
原书的 console 实验把七段文字绑上 [9,6,5,4,3,2,1],filter(d => d % 3 == 0)
留下第 1、2、5 段16。
filter 与 select(回调) 的细微差别在内部数组上:filter 把不通过的抠掉(数组变短),
select 用 null 占位(保留下标)。下标在合并、排序、删除等按位置操作时重要;
只读的静态场景,filter 更省17。
3. 主线例子:一次完整的 bind-join-update¶
现在走本章的核心。场景:一个 600×100 的空 SVG,我们绑三轮数据,每轮都在控制台里摊开选择器对象看。
第 0 轮:空选择¶
<svg> 里还没有圆,选到的是空选择——但注意它是合法对象,_parents 指向那个 <svg>。
这个「空 + 有父级上下文」的状态,是 join 的起点18。
第 1 轮:数据比元素多(enter)¶
绑 5 个数据:upd1 = sel1.data([5,10,15,20,25])。
data() 不碰 DOM——页面上还是空白;它返回一个新的更新选择(update selection),
内部三个数组此刻的状态是:
_groups: [ 空, 空, 空, 空, 空 ] ← 5 个空位(还没有元素可更新)
_enter: [ 占位, 占位, 占位, 占位, 占位 ] ← 5 个 EnterNode 占位对象,各存一项数据
_exit: [ 空, 空, 空, 空, 空 ]
图说:三个数组等大、互补——这是 D3 按下标管理更新的数据结构基础。
展开 EnterNode 能看到它存数据的 __data__ 属性。
执行 merged1 = upd1.join("circle") 之后:占位对象变成 5 个真 <circle> 挂到 <svg> 下,
三个内部数组合流,_enter/_exit 消失,_groups 里是 5 个圆。
此时这 5 个圆还没有任何属性——全部叠在原点 (0,0),半径 0,仍然不可见19。
加上属性,圆才出现:
function update() {
d3.select("svg").selectAll("circle")
.attr("cy", 50)
.transition()
.attr("cx", (d,i) => i * 100 + 50) // 横向排开:50, 150, 250, 350, 450
.attr("r", d => d); // 半径 = 数据值:5, 10, 15, 20, 25
}
update();
五个圆亮出来:圆心 y 全是 50,横坐标按序排开,半径正好是各自的数20。
第 2 轮:数据比元素少(exit)¶
重选现有元素:sel2 = d3.select("svg").selectAll("circle")(5 个)。
绑更小的数组:upd2 = sel2.data([19,15,26])。内部对账:
_groups: [ 圆1(绑19), 圆2(绑15), 圆3(绑26) ] ← 前 3 个对上了
_enter: [ 空, 空, 空 ]
_exit: [ 空, 空, 圆4, 圆5, 空 ] ← 后 2 个元素没人要
图说:默认按下标配对——新数组的第 0 项接管旧第 0 个元素,不管值对不对。
所以 19、15、26 分别绑到了原来半径 5、10、15 的圆上。
upd2.join("circle"):把 _exit 里的两个圆删掉(等价于 upd2.exit().remove()),
返回剩 3 个圆的合并选择。再调 update(),三个圆动画到新值:
原来的圆 4(半径 20)和圆 5(半径 25)消失,剩下的缩放到 19、15、2621。
第 3 轮:数据又变多(enter + update 同场)¶
现在 3 个圆在页面上,绑 6 个数据:upd3 = sel.data([12,15,9,3,15,18]):
_groups: [ 圆1(绑12), 圆2(绑15), 圆3(绑9), 空, 空, 空 ]
_enter: [ 空, 空, 空, 占位(3), 占位(15), 占位(18) ]
_exit: [ 空 × 6 ]
图说:前 3 个对上是「更新」,后 3 个数据没有元素是「新增」。
upd3.join("circle"):3 个占位对象变成新圆(追加在最后),与 3 个旧圆合并成 6 个圆的 _groups。
注意 join 返回的是合并选择,不是只含新圆的 enter 选择——
upd3.enter().append("circle") 只返回新圆,join() 是全体22。
再调 update(),六个圆排开:半径 12、15、9、3、15、18。
收敛成惯用写法¶
拆开看是为了理解,写代码时一次链式调用就够(三轮数据共用的就是这一段):
d3.select("svg").selectAll("circle")
.data(data) // 依次喂 [5,10,15,20,25] → [19,15,26] → [12,15,9,3,15,18]
.join("circle")
.attr("cy", 50)
.transition()
.attr("cx", (d,i) => i*100+50)
.attr("r", d => d);
data() 管对账,join() 管增删合并,后面的 attr/style 作用于合并后的全体——
第 01 章的那个简单写法,内部就是这整套流程23。
3.1 拆开 join:enter / update / exit 三个回调¶
join("circle") 是简写,等价于显式传入三个回调24:
.join(
enter => enter.append("circle"), // 新增:造元素(必须返回选择器)
update => update, // 更新:原样返回(也可加过渡)
exit => exit.remove() // 删除(返回值被忽略)
)
为什么要拆开?三个阶段的待遇可以不同。原书的演示:新圆绿色进场、更新的圆变蓝、 要删的圆先变红、停 ¼ 秒再删除:
.join(
enter => enter.append("circle").attr("fill","green").attr("cy",50),
update => update.transition().attr("fill","blue"),
exit => exit.transition().attr("fill","red").transition().remove()
).transition() // 合并后全体再做位置/半径动画
.attr("cx", (d,i) => i*100+50)
.attr("r", d => d);
如果新旧元素待遇相同,官方建议:enter 里设置一次,update 原样返回, 修改统一放在 join 之后对合并选择做(上面代码 join 后的 transition 就是)25。
3.2 key 函数:不按位置,按身份对账¶
默认对账按下标——新数组第 i 项永远接管旧第 i 个元素,所以「更新」总是前排元素, 「删除」总是末尾元素。但很多场景数据有身份:国家、用户、商品…… 换一批数据,你希望对得上的是同一个国家的元素,而不是「位置相同」的元素。
key 函数就是给每个数据项发身份证,作为 data() 的第二参数:
回到主线例子,加恒等 key 后同样三轮更新,内部数组完全不同26。看第 2 轮
(5 个圆 [5,10,15,20,25] → 3 个数据 [19,15,26],按值对账):
按下标(默认): 按 key(值即身份):
_groups: [圆(19), 圆(15), 圆(26)] _groups: [ 空, 圆(15), 空 ]
_exit: [ 空, 空, 圆4, 圆5 ] _exit: [ 圆(5), 空, 圆(20), 圆(25), 空 ]
→ 删末尾两个 _enter: [ 占位(19), 空, 占位(26), 空, 空 ]
→ 删 5、20、25 三个;新建 19、26 两个;
只有原来绑 15 的那个圆是「更新」
图说:identity key 下,只有值 15 同时存在于两批数据,所以它独占 update。
视觉后果:蓝色(更新)不再总是前排,红色(删除)不再总是末尾——元素跟着「身份」走。
key 不要求唯一:重复的 key 只有第一个生效,重复的元素进 _exit、重复的数据进 _enter27。
key 函数是原书 exercise 6.6 的考点:给两组线加 key,新线会插进旧线中间而不是追加在末尾。
3.3 datum():一对一的单点绑定¶
datum(value) 把一个值直接绑给选择里所有元素,不参与 join,不动下标,返回原选择28:
const dataset = [20,40,60];
dataset.forEach(d => d3.select("svg").append("rect").datum(d));
svg.selectAll("rect").attr("height", d => d).attr("width", d => d)
适用场景原书列了四类:数据本身是单个对象(不是数组)、只给一个元素绑值、
给 Canvas 路径绑数据(Canvas 没有 DOM 元素可绑)、从 HTML 的 data-* 属性里取数据。
其他情况,「用 data join 通常更简单也更高效」29。datum() 不带参数是读,datum(null) 解绑。
4. 其他方法:让链条更顺手的工具¶
4.1 each 与 call:把函数接进链里¶
链条写到一半,逻辑复杂起来怎么办?两个方法让函数调用不打断链30:
| 方法 | 执行次数 | 典型用途 |
|---|---|---|
each(回调) |
每个元素一次 | 在回调里 d3.select(this) 取当前元素,往它里面追加子结构 |
call(函数, …参数) |
整个选择一次(选择器作第一参数) | 复用同一段配置代码到多个选择 |
each 的经典场景:圆 + 标签要放进同一个组。链式 append 到圆之后就断了(当前选择变成圆,
而 SVG 的 <text> 不能塞进 <circle>);用 each,在每个组的回调里各自 append:
d3.select("svg").selectAll("g")
.data([3,5,9,15,24])
.join("g")
.attr("transform", d => `translate(${d*10}, 200)`)
.each(function(d) { // 普通函数:this 是当前 <g>
const group = d3.select(this);
group.append("text").text(d).attr("y",100);
group.append("circle").attr("r", d*2);
});
call 的经典场景:同一段「配置函数」喂给多组图表(坐标轴、缩放行为内部也大量用 call)31:
function config(group, scale, pos) {
group.attr("transform", d => `translate(${pos + d*scale*5}, 200)`);
group.append("text").text(d => d).attr("y",100);
group.append("circle").attr("r", d => d*scale);
}
d3.select("svg").selectAll("g#one") .data([1,2,4,7,12]).join("g").attr("id","one") .call(config, 2, 0);
d3.select("svg").selectAll("g#two") .data([3,5,9,15,24]).join("g").attr("id","two") .call(config, 2, 180);
d3.select("svg").selectAll("g#three").data([3,5,9]) .join("g").attr("id","three").call(config, 1, 500);
4.2 raise / lower / sort / insert / create:元素排序与游离子树¶
SVG 没有 z-index,谁后画谁在上——元素在 DOM 里的顺序就是叠放顺序。四个方法管这件事32:
| 方法 | 干什么 |
|---|---|
raise() / lower() |
把元素挪成父元素的最后一个/第一个孩子(置顶/置底) |
sort(比较函数) |
按绑的数据重排元素(DOM 顺序跟着变);比较器可用 d3.ascending/descending |
insert(tag, 选择器) |
像 append,但插到匹配元素之前(可用 circle[r='35'] 这类属性选择器定位) |
d3.create("svg:circle") |
造一棵游离的元素子树(不进页面),做好了再挂 |
raise 的典型交互:一堆叠在一起的圆,点哪个哪个 .raise() 到最上层33。
d3.create 的注意点:游离元素默认进 HTML 命名空间,SVG 元素要加前缀 d3.create("svg:circle");
挂回页面时 append 不认选择器,要传函数返回节点:svg.append(() => circle.node())34。
5. 调试:两个「代码明明执行了、图却不动」的经典 bug¶
这一节的 bug 都很阴险:join 流程完全正常、没有报错,元素就是不渲染。原书给的排查路径只有一句话: 看生成的代码(元素检查器)35。
Bug 1:类名断链¶
d3.select("svg").selectAll(".dot") // 选 .dot 类
.data([18,15,9,3,15,18])
.join("circle"); // 造出来的是 <circle>,没有 class="dot"!
页面原有 3 个 .dot 圆,join 补了 3 个新圆——但新圆没有 dot 类。
更新函数 d3.selectAll(".dot") 只能找到旧 3 个,新 3 个永远停在原点、半径 0,隐形36。
修法:join 之后立刻补类(官方推荐做法,类选择器配合 join 是原书认可的最佳实践):
Bug 2:元素挂错地方¶
d3.selectAll(全局函数)不保留父级上下文:新元素追加到了 <html> 根下,
不在 <svg> 里,继承不到 SVG 命名空间——浏览器不认 HTML 里的 <circle>,全部不渲染37。
修法:先选容器再选内容,父级上下文就对了:
两个 bug 合起来正好是本章两条铁律的反面:(1) join 出来的元素要带上选择时用的类名; (2) 更新用的选择必须和创建用的一样,在同样的父级上下文里。
6. 在线教程:卫星尺寸图(第二部分)¶
接着第 04 章末的教程:静态图升级成交互应用——控制面板选行星、换比例尺、悬浮提示, 用到本章的高级 join 和 key 函数。成品同样在 Observable 上可跑38。
7. 作者的判断与证据¶
| 判断 | 性质 | 依据 |
|---|---|---|
| 「join 优于手写 enter/exit/merge」 | 接口演进事实 | 老代码三段式被明确称为 legacy,join 是 v5+ 的官方写法39 |
| 「类选择器 + join 是推荐做法,但必须在 enter 时补类」 | 最佳实践 | 用 Bug 1 反证:类名断链导致元素隐形36 |
| 「内部属性(_groups 等)不是公开 API」 | 设计声明 | 原书「Important note」框明确警告5 |
8. 边界与局限¶
on()(事件)和transition()(动画)两个选择器方法本章故意没讲——分别在第 14、13 章40。- 选择器性能(大列表的 join 开销、key 函数的哈希成本)全书没有定量讨论。
_enter/_exit的内部结构属于实现细节,D3 版本升级可能变;本章的「看内部数组学机制」是教学手段,不是依赖契约。- 老代码里的
enter().append() + exit().remove() + merge()三段式只提到名字,迁移细节在仓库 Appendix/Migration41。
判断(我们的,不是书里的): 这一章最值得带走的心智模型是「对账」两个字: join 本质是把两个集合(数据、元素)做一次集合运算——默认按位置(类似数组下标连接), key 函数按身份(类似数据库主键连接)。第 2 轮里那种「更新只剩一个圆」的错配感, 正是从「位置连接」切到「身份连接」时的直觉落差;想通这个类比,join 就没有任何神秘的了。 如果错,会错在: 数据库连接的类比暗示 join 会「重排」已有元素——实际上 D3 从不为匹配 而移动 DOM 里的元素位置,新元素永远追加在末尾,顺序变化要靠 key + sort 显式完成。 类比只能用到「对账规则」这一层。
9. 可带走的¶
本章主线例子,一行走完: 空 SVG → data([5,10,15,20,25]).join("circle") 造 5 圆(半径 5/10/15/20/25,
cx 50–450)→ 换绑 [19,15,26],末尾 2 圆进 _exit 被删,剩 3 圆动画到新半径 →
再绑 [12,15,9,3,15,18],后 3 项经 _enter 补 3 个新圆,6 圆排开。加 key d => d 后,
对账按值不按位:只有 15 是「更新」,19/26 是「新建」,5/20/25 是「删除」。
d3.select / selectAll永远返回选择器对象,空手也返回——空选择是 join 的起点;- 选择器内部
_groups(元素)/_parents(父级上下文),调试可看,代码不碰; select传数据不留分组;selectAll留分组不传数据——append 前想清楚;- 回调三参数
d, i, nodes;箭头函数没有this,当前元素用nodes[i]; data()做对账,产出 update 选择;_enter是数据多出的占位,_exit是元素多出的;join("tag")= enter 追加 + exit 删除 + 合并返回;拆成三个回调可分阶段定制(进场/更新/离场各待遇不同);- key 函数 = 数据身份证;按身份对账后「更新」不再等于「前排元素」;
datum()单点绑定,适合非数组数据、Canvas、data-* 属性取值;each逐元素、call每选择一次,把函数接进链里不拆链;- 叠放顺序 = DOM 顺序:
raise/lower/sort/insert管层叠; - 隐形元素两大 bug:join 没补类名、全局 selectAll 丢了父级上下文——查元素检查器。
10. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 章节定位:d3-selection 最重要 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:5(搜「most important module」) |
| 全局选择函数与空选择 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:42(搜「d3.select(string)」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:96(搜「div.klm」) |
| node()/nodes() 与 canvas | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:127(搜「nodes()」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:142(搜「getContext」) |
| select 与 selectAll 的分组差别 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:165(搜「propagates data」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:191(搜「only supported by selectAll()」) |
| attr/style/text/classed/property | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:209(搜「attr(name)」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:283(搜「style() is recommended」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:291(搜「transform CSS property」) |
| 回调三参数与箭头函数 this | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:302(搜「up to three parameters」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:336(搜「arrow functions」) |
| filter vs select(保序) | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:385(搜「d % 3 == 0」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:394(搜「_groups: [p#n2」) |
| data()/datum()/join() 方法表 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:479(搜「_groups, _enter, and _exit」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:500(搜「join(tagname)」) |
| 内部属性非公开 API | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:589(搜「not part of the public API」) |
| 主线例子三轮 join | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:667(搜「sel1.size() == 0」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:674(搜「data([5,10,15,20,25])」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:683(搜「join("circle")」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:724(搜「data([19,15,26])」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:746(搜「data([12,15,9,3,15,18])」) |
| update() 与 transition | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:701(搜「function update()」) |
| 拆开的 join 三回调 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:779(搜「Controlling separate join steps」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:819(搜「attr("fill", "green")」) |
| key 函数与按值对账 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:855(搜「d => d.id」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:870(搜「identity key function」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:880(搜「Two new circles will be added」) |
| datum() | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:902(搜「one-to-one data binding」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:927(搜「typically used」) |
| each/call | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:968(搜「call(function,args)」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1012(搜「each(function (d)」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1034(搜「call(config, 2, 0)」) |
| raise/lower/sort/insert/create | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:1054(搜「bring to front」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1085(搜「bound to data in array index order」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1102(搜「insert("rect"」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1110(搜「d3.create("svg")」) |
| 两个 join bug | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:1139(搜「1-data-join-bug-1」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1179(搜「add this」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1197(搜「2-data-join-bug-2」) · text/13-ch06-chapter-6-selections-and-data-binding.txt:1209(搜「first get the right context」) |
| 在线教程第二部分 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:1221(搜「part 2」) |
| 章小结 | Selections and Data Binding | text/13-ch06-chapter-6-selections-and-data-binding.txt:1235(搜「Summary」) |
-
出处:「Selections and Data Binding」第 11–12 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:12,搜「essential to achieving proficiency」)。原文:「Mastering these subjects is essential to achieving proficiency in D3.」 ↩ -
出处:「Selections and Data Binding」第 532–534 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:533,搜「typing commands」)。本章大量示例通过浏览器控制台逐段演示,见原书各小节。 ↩ -
出处:「Selections and Data Binding」第 40–51 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:42,搜「d3.select(string)」)。函数表照原书 Table 6.1 转写。 ↩ -
出处:「Selections and Data Binding」第 94–100 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:96,搜「div.klm」;:99,搜「empty NodeList」)。原文:「A selection object that doesn't match any existing elements can be used to create and append new matching elements to the DOM tree after it is bound to a data array.」 ↩ -
出处:「Selections and Data Binding」第 586–590 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:589,搜「not part of the public API」)。原文:「While examining their contents may be useful for debugging purposes, they are not part of the public API and should never be accessed directly in your code.」 ↩↩ -
出处:「Selections and Data Binding」第 139–145 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:142,搜「getContext("2d")」)。原文:「Calling node() in the preceding example converts the selection into an HTMLCanvasElement object, from where you can call getContext().」 ↩ -
出处:「Selections and Data Binding」第 156–178 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:162,搜「selection.select(string)」)。方法表照原书 Table 6.3 转写。 ↩ -
出处:「Selections and Data Binding」第 191–195 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:191,搜「only supported by selectAll()」)。原文:「both of the following commands select the first <p> element in <div.abc>, but only the first sets it as parent」。 ↩ -
出处:「Selections and Data Binding」第 180–198 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:186,搜「_parents」)。Figure 6.2 的说明文字与截图对比见第 188 段。 ↩ -
出处:「Selections and Data Binding」第 202–247 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:209,搜「attr(name)」)。方法表照原书 Table 6.4 转写;「除 classed() 外都可以当 getter」见第 233 段(搜「used as getters」)。 ↩ -
出处:「Selections and Data Binding」第 283–299 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:283,搜「style() is recommended」)。原文:「When applying fills, strokes, and font styles, style() is recommended.」 ↩ -
出处:「Selections and Data Binding」第 290–295 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:294,搜「translate(25,25) rotate(45)」)。 ↩ -
出处:「Selections and Data Binding」第 301–309 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:302,搜「up to three parameters」)。 ↩ -
出处:「Selections and Data Binding」第 336–343 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:341,搜「nodes array and the current index」)。 ↩ -
出处:「Selections and Data Binding」第 356–375 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:361,搜「d % 2 == 0」;:375,搜「hidden」)。 ↩ -
出处:「Selections and Data Binding」第 379–389 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:383,搜「multiples of three」)。 ↩ -
出处:「Selections and Data Binding」第 391–402 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:401,搜「Indices are important」)。 ↩ -
出处:「Selections and Data Binding」第 660–670 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:667,搜「sel1.size() == 0」)。 ↩ -
出处:「Selections and Data Binding」第 672–697 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:683,搜「join("circle")」;:675,搜「EnterNode」)。 ↩ -
出处:「Selections and Data Binding」第 699–715 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:705,搜「i * 100 + 50」)。 ↩ -
出处:「Selections and Data Binding」第 717–739 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:724,搜「data([19,15,26])」;:732,搜「exit().remove()」)。 ↩ -
出处:「Selections and Data Binding」第 740–760 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:756,搜「not equivalent」)。原文:「The merged3 selection not equivalent to upd3.enter().append("circle"), as enter() returns a selection of the new circles, while join() returns the merged selection with all circles.」 ↩ -
出处:「Selections and Data Binding」第 762–773 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:763,搜「single chain」)。原文:「you don't have to break it down into separate selections and can write it all in a single chain」。 ↩ -
出处:「Selections and Data Binding」第 779–803 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:782,搜「shortcut for the following default」)。join("tag") 的等价展开式照原书第 784–800 段转写。 ↩ -
出处:「Selections and Data Binding」第 833–842 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:833,搜「apply the same styles」)。 ↩ -
出处:「Selections and Data Binding」第 849–900 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:869,搜「data([19,15,26], d => d)」;:880,搜「Two new circles will be added」)。 ↩ -
出处:「Selections and Data Binding」第 859–861 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:859,搜「not required to return unique values」)。原文:「Key functions are not required to return unique values, and if duplicates occur, only the initial value is considered.」 ↩ -
出处:「Selections and Data Binding」第 901–930 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:901,搜「One-to-one data binding」)。 ↩ -
出处:「Selections and Data Binding」第 927–930 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:930,搜「usually simpler and more efficient」)。 ↩ -
出处:「Selections and Data Binding」第 957–979 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:968,搜「call(function,args)」)。方法表照原书 Table 6.8 转写。 ↩ -
出处:「Selections and Data Binding」第 1018–1046 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1034,搜「call(config, 2, 0)」;:1045,搜「draw axes」)。 ↩ -
出处:「Selections and Data Binding」第 1048–1066 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1054,搜「bring to front」)。方法表照原书 Table 6.9 转写。 ↩ -
出处:「Selections and Data Binding」第 1068–1083 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1080,搜「circle.raise()」)。 ↩ -
出处:「Selections and Data Binding」第 1106–1125 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1110,搜「d3.create("svg")」;:1122,搜「circle.node()」)。 ↩ -
出处:「Selections and Data Binding」第 1131–1135 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1134,搜「common bugs」)。 ↩ -
出处:「Selections and Data Binding」第 1139–1180 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1151,搜「zero radius」;:1154,搜「class='dot'」;:1179,搜「attr("class", "dot")」)。 ↩↩ -
出处:「Selections and Data Binding」第 1184–1215 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1200,搜「SVG namespace」;:1212,搜「attr("class", "dot")」)。原文:「since HTML doesn't recognize the <circle> element, these circles won't be rendered」。 ↩ -
出处:「Selections and Data Binding」第 1221–1234 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1225,搜「control panel」;:1233,搜「Observable notebook」)。 ↩ -
出处:「Selections and Data Binding」第 844–845 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:844,搜「older D3 code」)。原文:「If you stumble upon older D3 code, instead of join(), it might use enter().append(), exit().remove(), and merge() to perform joins.」 ↩ -
出处:「Selections and Data Binding」第 1126–1128 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:1126,搜「selection methods were not covered」)。原文:「Two important selection methods were not covered in this chapter: on() for event handling and transition() for animations, since we already introduced their basic usage in Chapter 4.」 ↩ -
出处:「Selections and Data Binding」第 844–845 段(
text/13-ch06-chapter-6-selections-and-data-binding.txt:844,搜「older D3 code」)与「References and Additional Resources」第 130–131 段(text/28-ch20-chapter-20-references-and-additional-resources.txt:129,搜「Migration」)。 ↩