译者注:

原文: Mike Bostock (D3.js 作者) -- Nested Selections

译者: ssthouse

本文讲解的是关于 D3.js 中 d3-selection 的使用. d3-selection 是 d3 的核心所在, 它提供了一种和以往 Dom 操作数据操作 完全不同的思路, 让我们能非常优雅的进行数据可视化工作.
本文是 d3 作者对于 d3-selection 中 嵌套选择集 的讲解, 本人阅读后觉得很有启发, 所以翻译成中文, 希望对读者也能有所帮助.

译文

D3 的 选择集是分层级的, 就像 Dom 元素和数据集是可以有层级的一样. 比如说 Table:

<table><thead><tr><td>  A</td><td>  B</td><td>  C</td><td>  D</td></tr></thead><tbody><tr><td>  0</td><td>  1</td><td>  2</td><td>  3</td></tr><tr><td>  4</td><td>  5</td><td>  6</td><td>  7</td></tr><tr><td>  8</td><td>  9</td><td> 10</td><td> 11</td></tr><tr><td> 12</td><td> 13</td><td> 14</td><td> 15</td></tr></tbody>
</table>

如果让你只选中 tbody 元素中的 td, 你会如何操作? 直接使用 d3.selectAll('td') 显然会选中所有的 td 元素(包括 thead 和 tbody). 如果想只选中存在与 B 元素中的 A 元素, 你需要这样操作:

var td = d3.selectAll('tbody td')

除了上面那种方法, 你还可以先选中 tbody, 再选中 td 元素, 像这样:

var td = d3.select('tbody').selectAll('td')

因为 selectAll 会对当前选择集中的每个元素(如: tbody)选中其符合条件的子元素(如: td). 这种方法会得到和 d3.selectAll('tbody td') 相同的结果. 但如果你之后想要对同一父元素的选择集引申出更多的选择集的话 (比如区分选中 table 中的奇数行选择集和偶数行选择集), 这种嵌套选择集方式会方便的多.

嵌套中索引的使用

接着上面的例子, 如果你使用 d3.selectAll('td') , 你会得到一个平展的选择集, 像这样:

var td = d3.selectAll('tbody td')

平展的选择集缺少了层级结构: 不论是 thead 中的 td 还是 tbody 中的 td 全都被展开成了一个数组, 而不是以父元素进行分组. 这让我们想对每一行或是每一列的 td 进行操作变得很困难. 与此相反的, D3 的嵌套选择集能保存层级关系. 比如我们想以行的方式对选择集分组, 我们首先选中 tr 元素. 然后选中 td 元素.

var td = d3.selectAll('tbody tr').selectAll('td')

现在, 如果你想让第一列的所有元素变红, 你可以利用 index 参数 i:

td.style('color', function(d, i) {return i ? null : 'red'
})

上面的参数 i 是 td 元素在 tr 中的索引, 你还可以通过添加一个 j 参数来获得当前的行数的索引. 像这样:

td.style('color', function(d, i, j) {console.log(`current row: ${j}`)return i ? null : 'red'
})

嵌套和数据间的关联

层级结构的 Dom 元素常常是由层级结构的数据来驱动的. 而层级的选择集能更方便的处理数据绑定.
继续上面的例子, 你可以把 table 的数据表示为一个矩阵:

var matrix = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]
]

为了让这些数据绑定上对应的 td 元素, 我们首先将矩阵的每一行和 tr 绑定对应起来, 然后再将矩阵中一行的每一个元素和 tr 中的每一个 td 绑定起来:

var td = d3.selectAll('tbody tr').data(matrix).selectAll('td').data(function(d, i) {return d}) // d is matrix[i]

需要注意的是, data() 不仅可以传入一个数据, 它还可以传入一个 _返回一个数组的 function_. 平展的选择集通常对应的是单个数组, 是因为平展的选择集只有一个 group.

上面的 row 选择集是一个平展的选择集, 因为它是直接由 d3.selectAll 创建的:

var tr = d3.selectAll('tbody tr').data(matrix)

而 td 的选择集是嵌套的:

var td = tr.selectAll('td').data(function(d) {return d
}) // matrix[i]

data 传入的 操作函数给每一个 group 绑定了一个数组数据. d3 会对每一行 tr 调用操作函数. 因为父元素数据是矩阵, 所以操作函数在每次被调用时只是简单的返回矩阵中当前行的数据, 来和 tr 进行绑定.

嵌套中父节点作用

嵌套选择集有一个微妙但可能造成严重影响的副作用: 它会给每个 group 设置父节点. 父节点是选择集的一个隐藏属性, 它会在被调用 append 方法时使用, 将子元素添加到父节点的 Dom 元素当中. 比如: 如果你想通过下面的方式进行数据绑定操作, 你会得到一个 error:

d3.selectAll('table tr').data(matrix).enter().append('tr') // error!

上面的代码之所以会报错, 是因为默认的父节点是 html 元素, 你不能直接将 tr 元素添加到 html 元素中. 所以, 我们应该在进行数据绑定前, 先选择好父节点:

d3.select('table').selectAll('tr').data(matrix).enter().append('tr') // success

这种方式可以用来选择任意层级的嵌套选择集. 比如你想从头创建一个 table, 并填入上面矩阵中的数据, 你可以首选选中 body 元素:

var body = d3.select('body')

接下来在父节点 body 中添加一个 table:

var table = body.append('table')

接下来绑定矩阵数据, 创建 tr 元素. 因为 selectAll 是在 table 元素上进行调用的, 所以父节点是 table:

var tr = table.selectAll('tr').data(matrix).enter().append('tr')

最后我们以 tr 作为父节点, 创建 td 元素:

var td = tr.selectAll('td').data(function(d) {return d}).enter().append('td')

要不要使用嵌套选择集 ?

在 D3 中, select 和 selectAll 有一个很重要的区别: select 会继续使用当前存在的 group, 而 selectAll 总是会创建新的 group. 因此调用 select 能保存原有 selection 的数据, 索引位置, 甚至父节点.

比如, 下面的平展的选择集, 它的父节点仍然是 html 节点:

var td = d3.selectAll('tbody tr').select('td')

想要得到嵌套的选择集, 唯一的方法就是在已有的选择集的基础上, 调用 selectAll 方法. 这就是为什么数据绑定总是出现在 selectAll 之后, 而不是 select 之后.

这篇文章使用 table 作为例子仅仅是为了方便讲解层级结构. 其实 table 的使用并不是特别具有典型性. 其实还有许多其它 嵌套选择集的例子(点我查看, 点我查看)

就像 用 join 的方式思考 一样, 嵌套选择集同样使用了一种思想上完全不同的处理 Dom 元素数据 的思路. 这种思路刚开始可能很难理解, 但你一旦掌握了, 你就能驾轻就熟的使用 D3.js 来完成你的数据可视化任务了.

如果觉得不错的话, 不妨点下面的链接关注一下 : )

github 主页

知乎专栏

掘金

[译] D3.js 嵌套选择集 (Nested Selection)相关推荐

  1. d3.js中选择元素和绑定数据

    回顾一下如何选择元素 在 D3 中,用于选择元素的函数有两个: d3.select():是选择所有指定元素的第一个 d3.selectAll():是选择指定元素的全部 这两个函数返回的结果称为选择集. ...

  2. D3.js的v5版本入门教程(第五章)—— 选择、插入、删除元素

    D3.js的v5版本入门教程(第五章) 1.选择元素 现在我们已经知道,d3.js中选择元素的函数有select()和selectAll(),下面来详细讲解一下 假设我们的<body>中有 ...

  3. 数据可视化利器D3.js教程 API

    汇智网 http://xc.hubwiz.com/course/54fd40cfe564e50d50dcf284 D3.js 入门系列 - 选择元素和绑定数据 https://www.cnblogs. ...

  4. D3.js 教程: 使用 JavaScript 创建可交互的柱状图

    原文链接:D3.js Tutorial: Building Interactive Bar Charts with JavaScript 译者:OFED 最近,我们有幸参与了一个机器学习项目,该项目涉 ...

  5. D3.js中文版api-接口文档

    Api参考 此文档翻译自 API Reference (英语),版本为 2013-9-9 .不能保证文档的同步更新,因此,需要了解最新的开发特性,请移步英文版 API 参考 . d3 库所提供的所有 ...

  6. d3库(d3.js) 持续整理

    d3.js d3.js是基于数据操作文档的js库,集强力可视化组建与数据驱动型的dom操作手法于一身. 本质上是js,在数据可视化方面,d3将生成可视化的步骤精简到了几个简单的函数. 来源:http: ...

  7. D3.js中文版api

    D3js,一个做图例化展示的良好工具. d3 (核心部分) 选择集 d3.select - 从当前文档中选择一系列元素. d3.selectAll - 从当前文档中选择多项元素. selection. ...

  8. 03-03 创建和编辑AutoCAD实体(三) 使用选择集(2)

    4.DefineRules for Selection Filters定义选择集过滤器规则 You can limit which objects are selectedand added to a ...

  9. d3.js中的选择元素

    在 D3 中,用于选择元素的函数有两个: d3.select():是选择所有指定元素的第一个 d3.selectAll():是选择指定元素的全部 这两个函数返回的结果称为选择集. 怎么使用这2个函数呢 ...

最新文章

  1. Spring AOP源码分析(八)SpringAOP要注意的地方
  2. C++ Double Ended Queues(双向队列)
  3. Android使用ActionBar和ViewPager切换页面
  4. (十)Linux之等待队列
  5. Python学习总结(1)——编程准备和基本语法
  6. 使用nameko框架实现Python项目微服务化
  7. 如果把钢铁侠中的贾维斯系统换成现在的人工智能会怎么样?
  8. adb配置环境变量没有反应
  9. 计算机鼠标键盘没反应,终于找到为什么电脑鼠标键盘失灵了
  10. Docker日志查看命令
  11. php 伪静态是什么意思,php伪静态的写法是什么
  12. 给PDF文件添加图片的方法
  13. 中国未来5年最“吃香”的4大行业,市场需求大,堪比铁饭碗!
  14. 【研一周小结】第十周个人学习总结
  15. 在网页上实现大华视频监控摄像头在线
  16. 计算机电子电路原理图,简易电子琴设计电路图大全(八款模拟电路设计原理图详解) - 消费类电子电路图...
  17. vue中时间格式2021-11-21T12:30:00.000+00:00转换yyyy-MM-dd HH:mm:ss
  18. 真·抢显卡!四川一团伙持 40cm 长刀入室抢劫 50 余张显卡,总价值超 10 万元
  19. 射频功率的快速测量法
  20. 密探独家 | 访谈李开复:这三个行业会最先被 AI 颠覆

热门文章

  1. Mac下修改ZOC中文乱码
  2. win10中的ubuntu子系统忘记root密码如何修改
  3. 基于php的问答,thinkask
  4. 原型模式的应用场景_23中设计模式(上)
  5. 用tcc编译一个c语言写的简单代码hello world
  6. kafka消费并导出_如何使用Docker内的Kafka服务?消息服务测试实践篇
  7. MFC中如何从EDIT控件中获取文字
  8. 为什么你应该尝试全栈
  9. 用户模式 VS 内核模式(1)
  10. Windows下C/C++获取当前系统时间