安装
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 npm install d3-selection
加载
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 let d3Selection = require("d3-selection");
该对象下面包含:
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154625151-179933278.png)
概述
d3的选择器是通过选择元素并为之绑定数据来转变DOM的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.select("body")
返回一个选集。
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154625526-1804837821.png)
选集中的方法返回的也是当前选集,这为链式调用提供了方便。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p") 2 .attr("class","graf") 3 .style("color","red");
按照惯例,如果选集的方法返回的是当前的选集,用4个空格缩进,如果返回的是新选集,则用两个空格缩进。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.select("body") 2 .append("svg") 3 .attr("width",960) 4 .attr("height",500)
如append返回的是新选集,而attr返回的是当前选集。
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154626073-1176940790.gif)
选集是不可变的,所有影响选集中元素的选集方法将返回一个新的选集而非修改当前选集,如append。但是请注意,选集中的元素是可变的。
选择元素
selection
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 functionSelection(groups, parents){ 2 this._groups = groups; 3 this._parents = parents; 4 } 5 function selection(){ 6 returnnewSelection([[document.documentElement]], root); 7 }
从源码中可以看出,selection()就是文档的根元素,即html节点。它可以用来可以被用来测试一个选择集是否是d3.selection的实例或者扩展选集的属性:
首先我们为选集添加一个新的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selection.prototype.checked =function(value){ 2 return arguments.length<1?this.property("checked"):this.property("checked",!!value); 3 };
然后使用这个新的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.select("#ckb").checked(true);
selection是分组存储的:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <p><b>aa</b><b>aa1</b></p> 2 <p><b>bb</b><b>bb1</b></p> 3 <p><b>cc</b><b>cc1</b></p> 4 <script> 5 console.log(d3.selectAll("p")); 6 </script>
此时只有一个分组。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <p><b>aa</b><b>aa1</b></p> 2 <p><b>bb</b><b>bb1</b></p> 3 <p><b>cc</b><b>cc1</b></p> 4 <script> 5 console.log(d3.selectAll("p").selectAll("b")); 6 </script>
现在有3个分组。
由此可见, selection.select 不改变分组,而 selection.selectionAll 会创建新的不同分组(不会影响原有分组),分组依据selection的每个选项。
如果 selector是一个方法,则它会在已经选中的每个元素上调用一次,并传递当前元素绑定的数据(d), 索引(i), 以及当前的分组 (nodes), this 指向当前的元素。这个方法一定要返回一个元素或null。最终将所有返回的元素选中作为selection.select(selector)的结果。比如选中每个p元素的上一个兄弟节点:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var previous = d3.selectAll("p").select(function(){ 2 returnthis.previousElementSibling; 3 });
selection.filter(filter)
filter可以为一个CSS选择字符串或者一个函数。
过滤选择集,返回一个新的选择集。会对选择集中的每个元素进行调用filter方法(或匹配CSS字符串),filter返回true时,才保留这个元素。filter方法传递当前元素绑定的数据d,索引i以及当前的分组nodes,this指向当前的元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var even = d3.selectAll("tr").filter(":nth-child(even)"); 2 //等价于( the indexes may be different) 3 var even = d3.selectAll("tr:nth-child(even)");
selection.merge(other)
返回一个新的选集,将当前选集与other选集合并。返回选集的分组数和parent个数和当前选集一样。它的主要作用是填充当前选集中的占位符。因此在d3中被用来合并当data-jon后的enter选集(enter选集即占位符)和update选集,在进行enter和updae之后将两个选择集合并而不需要其他的操作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var circle = svg.selectAll("circle").data(data)// UPDATE 2 .style("fill","blue"); 3 circle.exit().remove();// EXIT 4 circle.enter().append("circle")// ENTER 5 .style("fill","green") 6 .merge(circle)// ENTER + UPDATE 7 .style("stroke","black");
circle.enter().append("circle")只是提示d3添加占位符,svg.selectAll("circle").data(data)去更新选集,然后更新后的操作就是对两个都有效果了。
该选集并没有设计去合并两个任意的选集,如果当前选集和other选集在相同的索引下都有非null元素,那么当前选集被返回,而other选集被忽略。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <p><b>aa</b><b>aa1</b></p> 2 <p><b>bb</b><b>bb1</b></p> 3 <p><b>cc</b><b>cc1</b></p> 4 <div>div1</div> 5 <div>div2</div> 6 <div>div3</div> 7 <script> 8 console.log(d3.selectAll("p").merge(d3.selectAll("div"))); 9 </script>
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154630541-1172057538.png)
d3.matcher(selector)
根据指定的selector返回一个函数,这个函数可以接受一个元素,并判断这个元素是否满足selector,如果满足则返回true否则返回false。这个方法可以被用在selection.filter. 比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var div = selection.filter("div");
它等价于
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var div = selection.filter(d3.matcher("div"));
d3.window(node)
返回node所属的window对象。如果 node 是一个节点,则返回所属的文档。如果 node 是一个文档,则返回这个文档
改变元素
selection.attr(name[, value])
如果指定了value参数则这个方法就是设置,如果只有name参数则是获取,下同。
设置或获取元素的name属性。
value可以是一个变量,也可以是一个方法,如果是方法,则会执行这个方法,并传递当前元素绑定的数据 (d), 索引(i), 以及当前分组(nodes), this指向的是当前的元素,将方法的返回值作为name对应的值。
name可能是一个带前缀的属性,比如xlink:href来指定href属性的命名空间。
selection.classed(names[, value])
设置或获取指定的类名状态,如果指定了value则根据value值启用或关闭类。如果没有指定value则相当于查询指定类的状态。name可以同时指定多个,使用空格隔开。比如为选择集添加foo和bar两个类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.classed("foo bar",true);
如果value是一个方法,则根据返回值决定是添加还是移除某个类。比如随机的设置或移除foo类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.classed("foo",function(){returnMath.random()>0.5;});
如果没有指定value则当且仅当第一个非null元素使用了对应的类时返回true。
selection.style(name[, value[, priority]])
如果指定了value,则设置对应的样式为value。value可以为变量,也可以为一个函数,函数会传递当前元素绑定的数据d,索引i以及当前的分组groups。在函数内部this指向当前的元素,函数的返回值作为样式的值。可选的priority可以设置为默认的null或字符串important (没有感叹号).
如果没有指定value则返回当前选择集第一个非null元素经过计算后的样式值,要注意计算后的值 可能与设置值不同,尤其是使用了速写方法(比如font样式代替标准的font-size,font-face样式,等等).
注意:与SVG的属性不同,CSS的样式通常有单位,比如3px是一个笔画宽度单位,而3不是。一些浏览器会隐式为数值类型指定px单位,但是有些则不会,比如:IE不指定单位时会出现 “invalid arguments” 错误!
selection.property(name[, value])
一些HTML元素有些属性不能使用attribute或styles来查询或设置。比如form表单的text元素值以及checkbox的checked值。这时候就需要使用selection.property(name[,value])来设置或读取。
按照惯例,设置还是获取取决于有没有指定value. value可以是变量也可以是函数,函数传递d,i,nodes为参数,this指向当前的元素。
selection.text([value])
设置或获取text content内容。可以使用这个方法清空元素内容。
selection.html([value])
设置或获取inner HTML内容。可以使用这个方法清空元素内容。
使用 selection.append 或 selection.insert 来创建元素,selection.html 也支持手动插入HTML字符串来实现插入或添加元素,但是明显更复杂.
selection.append(type)
如果type是一个字符串,则在选择集中的每个元素的子元素末尾追加一个元素。如果selection是一个enter selection,则将元素追加到元素末尾,作为选择集中元素的下一个兄弟节点。enter操作可以将数据与DOM元素的顺序对应,当然也可以使用效率更慢一点的selection.order操作进行手动排序。
此外,type还可以是一个方法,则会传递d,i,nodes为参数,并在函数内部返回一个元素。这个元素类型可以根据实际需要选择。换句话说,就是如果type是一个方法的话,添加的元素就不一定是同一种类型。比如为每个p元素中添加一个div元素:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p").append("div");
等价于
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p").append(function(){ 2 return document.createElement("div"); 3 });
等价于
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p").select(function(){ 2 returnthis.appendChild(document.createElement("div")); 3 });
在上述例子中,都返回了一个包含添加元素的新的选择集。新的元素继承了当前元素的数据。
有些元素需要命名空间前缀,比如svg:text表示SVG命名空间中的text。关于命名空间更多信息参考 namespaces. 如果没有指定命名空间,那元素将会继承父元素的命名空间。
selection.insert(type, before)
type和before都可以为String或function
如before为
:first-child
表示将新元素type插入到第一个子节点上。如果为函数的话,type函数返回要插入的元素,before函数返回要在其之前插入type节点的孩子元素。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p").insert("div");
等价于
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
d3.selectAll("p").insert(function(){ return document.createElement("div"); });
等价于
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 d3.selectAll("p").select(function(){ 2 returnthis.insertBefore(document.createElement("div"),null); 3 });
和append一样,insert也会返回一个新的选集,该选集包含所有没添加的元素。每个元素继承了当前元素的data。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var data =[1,2,3]; 2 console.log(d3.select("body").selectAll("div").data(data).enter()); 3 console.log(d3.select("body").selectAll("div").data(data).enter().insert("p").text(function(d){return d;}));
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154949057-1347226295.png)
selection.remove()
从文档中将选择集移除,并返回移除的选择集。
selection.sort(compare)
返回一个新的经过排序的选择集。参数为一个比较方法。
比较方法默认是ascending,传递两个元素的数据a和b.
selection.order()
重新插入元素到文档中,以使每个组的文档顺序与选择顺序匹配。如果数据顺序已经排好等价于调用selection.sort但是更快.
selection.raise()
重新将元素插入到对应父元素,作为父元素的最后一个子元素。相等于:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.each(function(){ 2 this.parentNode.appendChild(this); 3 });
selection.lower()
重新将元素插入到对应父元素,作为父元素的第一个子元素,相当于:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.each(function(){ 2 this.parentNode.insertBefore(this,this.parentNode.firstChild); 3 });
d3.creator(name)
根据指定的元素名,返回一个可以创建对应子元素的方法。比如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.append("div");
等价于:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 selection.append(d3.creator("div"));
Joining Data
selection.data([data[, key]])
为选集中的元素绑定数组类型的数据,然后返回一个新的选集,它代表update selection:所有元素成功的绑定到了data,并且该update selection被定义了enter和exit操作(因为数据个数未必与元素个数一致)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var data =[1,2,3]; 2 console.log(d3.select("body").selectAll("div")); 3 console.log(d3.select("body").selectAll("div").data(data));
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154635135-693423171.png)
可以看到,data-join之后,_groups下面的分组出现了和数组个数相同的占位符。此时页面中并没有div所有,第一个分组中内容是空的,因为数组长度为3所以占位符有3个。因此需要enter和exit操作来维护。
data必须和选集的分组相匹配。只有一个分组时,就是一维数组,有多个分组时就得用二维或多维数组了:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var matrix =[ 2 [11975, 5871,8916,2868], 3 [1951,10048,2060,6171], 4 [8010,16145,8090,8045], 5 [1013, 990, 940,6907] 6 ]; 7 var tr = d3.select("body") 8 .append("table") 9 .selectAll("tr") 10 .data(matrix) 11 .enter().append("tr"); 12 var td = tr.selectAll("td") 13 .data(function(d,i){ 14 return d; 15 });
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154635760-778810437.png)
td是有四个分组。那么data也必须是一个二维数组,在这里td有多个分组,那么它的data就应该是一个函数,这个函数将按顺序在每个group中执行。该函数有两个参数:
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154636229-2100704149.png)
第一个参数为对应每个数组,第二个参数为这个一维数组在二维数组中的索引。
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154637026-1541754804.png)
selection.selectAll会返回一个新的选集,该选集拥有多个分组。这些分组目前的长度都为0。表示每个分组都没有选择到一个元素。
接下来我们绑定数据:
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154637463-369761298.png)
从上图我们可以看出,每个分组的长度都变为4了,但是这时还没有一个元素,相当于限定了大小的空盒子。只是起到占位效果。这便是data的作用,它将元素和数据绑定,这里就限定了,每个选集中只能有4个元素,多余的元素会被删除(exit),缺少的元素会被添加(enter)。d3就是利用这个逻辑来维持数据与元素的一致性。
接下来,添加数据:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var td = tr.selectAll("td") 2 .data(function(d,i){ 3 return d; 4 }) 5 .enter().append("td") 6 .text(function(d){return d;});
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154638026-646253575.png)
enter().append()的作用就是寻找占位符,然后填充占位符。当然这里的append也可以为insert。循序遍历选集,遇见占位符就执行append或insert。
key函数用于指定数据和元素之间的对应关系,如果没有指定,则第一个数据对应第一个元素,第二个数据对应第二个元素……
If multiple elements have the same key, the duplicate elements are put into the exit selection; if multiple data have the same key, the duplicate data are put into the enter selection.
正确的书写逻辑应该为:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <body> 2 <div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div> 3 </body>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var data =[ 2 {name:"Locke", number:11}, 3 {name:"Reyes", number:3}, 4 {name:"Ford", number:5}, 5 {name:"Jarrah", number:2}, 6 {name:"Shephard", number:4}, 7 {name:"Kwon", number:7} 8 ]; 9 var selection = d3.selectAll("div").data(data);//update 10 selection.exit().remove(); 11 selection.enter().append("div").merge(selection).text(function(d){return d.name;}).style("background","yellow");
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154638573-1296843383.png)
![](https://images2015.cnblogs.com/blog/692756/201703/692756-20170313154638948-1273348690.png)
原来我们创建的11个div现在只剩下6个了。
注意在append后一定要merge,将enter选集和update选集合并,因为后面的操作是针对enter选集的,而不是update选集的操作。此时enter选集为空,则用update选集来填充enter选集。