这里转载一个非常经典的关于D3数据绑定的帖子,由D3作者自己写的,非常棒,以至于我忍不住全文copy到此。

原文地址

Thinking with Joins

Say you’re making a basic scatterplot using D3, and you need to create some SVG circle elements to visualize your data. You may be surprised to discover that D3 has no primitive for creating multiple DOM elements. Wait, WAT?

Sure, there’s the append method, which you can use to create a single element.

Here svg refers to a single-element selection containing an<svg> element created previously (or selected from the current page, say).

svg.append("circle")
.attr("cx", d.x)
.attr("cy", d.y)
.attr("r", 2.5);

But that’s just a single circle, and you want many circles: one for each data point. Before you bust out a for loop and brute-force it, consider this mystifying sequence from one of D3’s examples.

Here data is an array of JSON objects with x and y properties, such as: [{"x": 1.0, "y":1.1}, {"x": 2.0, "y":2.5}, …].

svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 2.5);

This code does exactly what you need: it creates a circle element for each data point, using the x andy data properties for positioning. But what’s with the selectAll("circle")? Why do you have to select elements that you know don’t exist in order to create new ones? WAT.

Here’s the deal. Instead of telling D3 how to do something, tell D3 what you want. You want the circle elements to correspond to data. You want one circle per datum. Instead of instructing D3 to create circles, then, tell D3 that the selection "circle" should correspond to data. This concept is called the data join:

Data points joined to existing elements produce the update (inner) selection. Leftover unbound data produce the enter selection (left), which represents missing elements. Likewise, any remaining unbound elements produce the exit selection (right), which represents elements to be removed.

Now we can unravel the mysterious enter-append sequence through the data join:

  1. First, svg.selectAll("circle") returns a new empty selection, since the SVG container was empty. The parent node of this selection is the SVG container.

  2. This selection is then joined to an array of data, resulting in three new selections that represent the three possible states: enterupdate, and exit. Since the selection was empty, the update and exit selections are empty, while the enter selection contains a placeholder for each new datum.

  3. The update selection is returned by selection.data, while the enter and exit selections hang off the update selection; selection.enter thus returns the enter selection.

  4. The missing elements are added to the SVG container by calling selection.append on the enter selection. This appends a new circle for each data point to the SVG container.

Thinking with joins means declaring a relationship between a selection (such as "circle") and data, and then implementing this relationship through the three enterupdate and exit states.

But why all the trouble? Why not just a primitive to create multiple elements? The beauty of the data join is that it generalizes. While the above code only handles the enter selection, which is sufficient for static visualizations, you can extend it to support dynamic visualizations with only minor modifications for update and exit. And that means you can visualize realtime data, allow interactive exploration, and transition smoothly between datasets!

Here’s an example of handling all three states:

var circle = svg.selectAll("circle")
.data(data); circle.exit().remove(); circle.enter().append("circle")
.attr("r", 2.5); circle
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });

To control how data is assign­ed to elements, you can pro­vide akey function.

Whenever this code is run, it recomputes the data join and maintains the desired correspondence between elements and data. If the new dataset is smaller than the old one, the surplus elements end up in the exit selection and get removed. If the new dataset is larger, the surplus data ends up in theenter selection and new nodes are added. If the new dataset is exactly the same size, then all the elements are simply updated with new positions, and no elements are added or removed.

Thinking with joins means your code is more declarative: you handle these three states without any branching (if) or iteration (for). Instead you describe how elements should correspond to data. If a given enterupdate or exit selection happens to be empty, the corresponding code is a no-op.

Joins also let you target operations to specific states, if needed. For example, you can set constant attributes (such as the circle’s radius, defined by the "r" attribute) on enter rather than update. By reselecting elements and minimizing DOM changes, you vastly improve rendering performance! Similarly, you can target animated transitions to specific states. For example, for entering circles to expand-in:

circle.enter().append("circle")
.attr("r", 0)
.transition()
.attr("r", 2.5);

Likewise, to shrink-out:

circle.exit().transition()
.attr("r", 0)
.remove();

Now you’re thinking with joins!

最新文章

  1. java中servlet的各种路径
  2. C语言字符输出格式化
  3. deeplab hole algorithm
  4. JavaScript - 事件流
  5. SQL删除重复的记录(只保留一条)
  6. Linux查找文件
  7. 用C语言写个程序推算出是星期几?(用泰勒公式实现)
  8. excel函数
  9. Marquee滚动字幕设置(转)
  10. Python的ASCII, GB2312, Unicode , UTF-8 相互转换
  11. Python学习笔记8-类的继承 、深度优先、广度优先
  12. POJ 1002 487-3279 Trie解读
  13. memcached 缓存数据库应用实践
  14. 51Nod 1326 遥远的旅途
  15. DSAPI 调用串口选择界面
  16. SRM465
  17. 微信小程序开发 [00] 写在前面的话,疯狂唠唠
  18. oracle收购Mysql后,Mysql的安装配置方法
  19. About Game Controllers
  20. Android Button事件处理

热门文章

  1. eclipse自动补全的设置
  2. sql 脚本编写之路 常用语句(一)
  3. Linux中find常见用法示例
  4. Java Abstract Class &amp; Interface
  5. python smtp 群发邮件
  6. 常见http错误码解读
  7. Netty 实现 WebSocket 聊天功能
  8. 读取图像,LUT以及计算耗时
  9. MFC双缓存技术代码
  10. 【新手出发】从搭虚拟机开始,一步一步在CentOS上跑起来.Net Core程序