d3js源码解析,selection模块(三),数据是如何绑定到元素上的?

网友投稿 275 2022-09-16

d3js源码解析,selection模块(三),数据是如何绑定到元素上的?

selection模块结构

选择元素修改元素加入数据(本章的内容)处理事件控制流局部变量命名空间

添加数据

这部分开始介绍Join,讲解的两个文章​​selection.join notebook​​​​Thinking With Joins​​

selection.data([data[, key]])

讲述数据数组绑定到元素上,返回绑定成功的selection表示update状态,同时也定义了enter,exit状态在返回的选择器上,数据可以为任意数组(数字,对象数组…)或者一个返回数组函数,存储在选择器的__data__属性中,从而使数据具有“粘滞性”。 结合join、enter、exit、append、remove,可以以数据驱动的方式添加、更新、删除元素,如下例子从matrix中创建html:

const matrix = [ [11975, 5871, 8916, 2868], [ 1951, 10048, 2060, 6171], [ 8010, 16145, 8090, 8045], [ 1013, 990, 940, 6907]];evs.select("body") .append("table") .selectAll("tr") .data(matrix) .join("tr") .selectAll("td") .data(d => d) .join("td") .text(d => d);

再该例子中,通过matrix的长度添加table行数。 如果没有指定key,则按数本身据顺序进行添加,如果key为函数,为每个元素执行该函数返回的字符串添加为当前元素(这里就只源码中的keyValue,类似于一个标记位),并把数据节点合并到enter中。data源码解析如果只看文档真是一头雾水,这里设计的原则就是为选择器添加了几个数组类属性,目前现在可以理解为data、enter、update、exit,当我们data一个数据的时候,会先根据data中的数据分别向这三个数组中添加元素,数据驱动的含义也在这里,update为更新过的数组,即原来的该节点的数据中和新加入的data冲突了,更新原来已有的节点已绑定数据的数据,enter就是data中去掉了更新的那部分剩余的数据,相当于添加新元素节点,exit会存放哪些无法与数据匹配上的,或数据长度就那么多,多余出来的原先存在的节点,相当于删除掉哪些节点了。当然最后作者删掉了update,因为这只是一个过程变量,不需要进行存储了。 你可以简单理解为enter会向原节点添加进去绑定数据,以方便以该数据驱动添加节点,exit数组存储的则为删除与数据不匹配的节点,update就是最后的返回值。源代码:不想看就跳过,但我觉着看一下代码就豁然开朗了:

import {Selection} from "./index.js";import {EnterNode} from "./enter.js";import constant from '../constant.js';var keyPrefix='$'; //防止像__proto__这样的key//不输入key的情况function bindIndex(parent,group,enter,update,exit,data){ var i=0, node, groupLength=group.length, dataLength=data.length; //这里处理可以添加数据的元素,即按传入数据data的长度向元素添加数据 //把非空节点加入update,所以这里update的含义可以说是可以被更新的节点数组 //把空节点加入enter for(;i= i1) i1=i0+1;//设置i1的初始值 while(!(next = updateGroup[i1]) && ++i1 < dataLength) ; previous._next = next || null; } } } update=new Selectoin(update,parent); update._enter = enter; update._exit=exit; return update;}

例如这个文档:

通过key函数添加数据:

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是否存在,返回name或id,以为当前选择器元素没有绑定过数据,元素上的数据(datum)即上面代码证d为空,如果元素绑定过了数据,则d为费空。 更新(update)和添加(enter)以数据顺序进行,删除状态(exit)保留原有的顺序,也许在指定了key时选择器中的元素顺序与文档中的元素顺序不一致,此时需要用前面提到的selection.order或sort对文档排序,关于key函数的更多理解​​​Let’s Make a Bar Chart, II​​​​Object Constancy​​ 如果没有指定data,则返回选择器元素的数据数组,该方法不能用来清除绑定数据,使用selection.datum.

selection.join(enter[, update][, exit])

根据之前绑定的数据data,对元素添加、删除、重新排序,是enter、exit、append、remove、oreder的显示替换。代码实现也是封装的这些函数。用法如下

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

并且可以传入函数控制每个操作:

svg.selectAll("circle") .data(data) .join( enter => enter.append("circle").attr("fill", "green"), update => update.attr("fill", "blue") ) .attr("stroke", "black");

换可以通过第三个函数删除,返回值会合并enter和update并返回通过分隔enter和update,以及在data中添加key函数,可以最小化对dom的更改以优化性能。 还可以通过在enter、update、exit中创建过渡来设置动画,为避免破坏方法链用selection.call创建过渡,或返回一个未定义的enter、update组织合并。

selection.enter()

返回选择器的enter状态,对于每个绑定好的数据(datum)没有对应DOM元素的占位符节点,如果没有selection.data掉用后使用,返回值为空。 选择器的enter状态通常用于创建与新数据缺失的元素,如下根据数组数据创建div:

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

如果body为空,该程序会创建6个div,根据数组数据的顺序,将其文本内容指定为关联数据(强制为字符串类型)。

4
8
15
16
23
42

从概念上讲,enter桩体的占位符是指向父元素的指针(上面例子中为body),该方法通常用于添加元素,添加后与update状态的选择器合并,使得应用于enter和update两个状态。 enter的实现原理是在enter.js中定义了一个enterNode对象,给选择器添加这个对象,前面提到的进入enter状态,就是返回一个新的选择器,这个选择器构造是传入的是上一条链路选择器的._enter,及其父节点。源码如下

import sparse from './sparse.js'import {Selection} from '.index.js'export default function () { return new Selection(this._enter || this._groups.map(sparse),this._parents);}//在这里定义了enternode对象 这里也是构造函数加原型模式export function EnterNode(parent,datum) { this.ownerDocument = parent.ownerDocument; this.namespaceURI=parent.namespaceURI; this._next = null; this._parent = parent; this.__data__=datum;}EnterNode.prototype={ constructor:EnterNode, appendChild:function(child){ return this._parent.insertBefore(child, this._next); }, insertBefore:function(child,next){ return this._parent.insertBefore(child,next); }, querySelector:function(selector){ return this._parent.querySelector(selector); }, querySelectorAll:function(selector) { return this._parent.querySelectorAll(selector); }};

selection.exit()

返回删除的selection元素,文档中没有被添加数据的节点。通常用于添加新数组前删除旧数据与多余元素。

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

data操作根据前面enter中的数据已经传入了[4, 8, 15, 16, 23, 42],新数据重复的有4,8,16,因此update这三个,可以使用enter添加1,2,32三个新元素。

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

删除旧数据中的15,23,42:

div.exit().remove();

现在文档是这样:

1
2
4
8
16
32

这里DOM的顺序与数据一致因为新旧数据都是一样的顺序,如果新加入的顺序不同,使用selection.order重新排序。

selection.datum([value])

获取或设置每个元素的绑定数据,这种方法不selection.data不同。不会join,也不影响enter和exit,其内部实现实际上是this.node().__data__直接获取或设置数据。 在指定了value的情况下,如果是常量则赋值,函数的话返回值设置为数据(在每个节点上数据绑定的名称为__data__),为null会删除该元素绑定的数据。 如果没有指定值,返回第一个非空节点绑定的数据,这在只有一个节点时很有用, 此方法在对于H5中的自定义属性非常有效,例如给定如下元素:

  • Shawn Allen
  • Mike Bostock

通过此方法将元素上绑定的数据设置为内置dataset属性。

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

总结

这一份是对数据绑定原理的解析,以及数据的几个状态,当我们分析数据时,最有效的几个操作添加数据、删除数据、更新数据,d3的作者用这几个简答的api全部解答了,换绑定到了dom上,使得dom可以根据数据进行同样的添加删除更新。其中基本的原理还视在selection对象上,我们给selection添加了这么几个属性enter(存储添加元素的数据节点数组)、exit(存储删除了的节点数组)、update(这个是根据新数据更新的数据绑定的节点数组)以及__data__(单个节点上的数据属性,保存数据内容)。这几个api中,data是必须的,data后可以对selectin进行enter、exit操作,以及datum查看或设置data值,以及一个抽象出来的join函数,简化enter、exit操作优化了速度。 高层接口:在我使用时就可以直接使用data.enter.进行数据绑定,其他操作在需要时添加即可。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:国家卫健委:新增本土确诊病例17例 其中吉林8例!
下一篇:tcpip协议概述
相关文章

 发表评论

暂时没有评论,来抢沙发吧~