d3js selections深入理解
D3 selections选择DOM元素以便可以对这些dom元素做相应的操作,比如:更改其style,修改其属性,执行data-join操作,或者插入、删除相应elements
比如,如果给定5个circles:
我们可以使用d3.selectAll来选中所有的circles,并且通过.style和.attr来修改其样式或者属性
d3.selectAll('circle') .style('fill', 'orange') .attr('r', function() { return 10 + Math.random() * 40; });
Making selections
D3有两个函数来执行选择操作:d3.select和d3.selectAll
d3.select进选中第一个匹配的元素。而d3.selectAll则选中所有匹配的元素。这两个函数都接收一个参数来指定选择器字符串:和css选择器相同语法 selector string.
比如我们使用css class类名来选择
d3.selectAll('.item').
Modifying elements
一旦我们完成了选择得到了selection,则我们可以使用selection的以下函数:
Name | Behaviour | Example |
---|---|---|
.style | Update the style | d3.selectAll('circle').style('fill', 'red') |
.attr | Update an attribute | d3.selectAll('rect').attr('width', 10) |
.classed | Add/remove a class attribute | d3.select('.item').classed('selected', true) |
.property | Update an element's property | d3.selectAll('.checkbox').property('checked', false) |
.text | Update the text content | d3.select('div.title').text('My new book') |
.html | Change the html content | d3.select('.legend').html('<div class="block"></div><div>0 - 10</div>') |
需要注意的是,无论是select还是selectAll返回的selection,执行上面的操作都将对所有选中的元素产生影响。
Updating selections with functions
除了传入常量值到对应attr,style函数外,我们也可以传入一个函数,,该函数输出值作为attr, style最终设定的值:
d3.selectAll('circle') .attr('cx', function(d, i) { return i * 100; });
该函数将自动接收(d,i)
. d
是joined在该元素上的数据 (参考 data joins section) 而 i
则是该元素在selection中对应的index
如果我们希望根据其在selection中的index来更改selection中的元素对应的x轴位置,比如我们希望水平布置rect元素在不同的位置(而不是堆砌在一起):
d3.selectAll('rect') .attr('x', function(d, i) { return i * 40; });
大多数情况下,传入一个匿名函数就好,但是我们也可以使用命名的
function positionRects(d, i) { return i * 40; } d3.selectAll('rect') .attr('x',
Handling events
我们可以通过selection.on()来为selection中的elements添加事件处理函数,而这个事件处理函数,同样地会传入d和i两个参数以方便处理。同样地,d是本元素joined data,i则是本元素在selection中的index值,下面是列出的主要event时间
Event name | Description |
---|---|
click | Element has been clicked |
mouseenter | Mouse pointer has moved onto the element |
mouseover | Mouse pointer has moved onto the element or its children |
mouseleave | Mouse pointer has moved off the element |
mouseout | Mouse pointer has moved off the element or its children |
mousemove | Mouse pointer has moved over the element |
比如下面我们写一个小的代码,实现当点击后就修改其text指明是第几个元素被点击
d3.selectAll('circle') .on('click', function(d, i) { d3.select('.status') .text('You clicked on circle ' + i); });
需要注意的是,在事件回调函数中,this的值是DOM 元素本身,和selection无关!如果要使用d3对该dom操作,必须先做select操作:
d3.selectAll('circle') .on('click', function(d, i) { d3.select(this) // this指被点击的dom元素,而不是d3 selection! .style('fill', 'orange'); });
Inserting and removing elements
我们看一个例子:3个g元素,每个g都包含了一个circle:
<g class="item" transform="translate(0, 0)">
<circle r="40" />
</g>
<g class="item" transform="translate(120, 0)">
<circle r="40" />
</g>
<g class="item" transform="translate(240, 0)">
<circle r="40" />
</g>
我们可以插入一个 text
element到每个g元素中:
d3.selectAll('g.item')
.append('text')
.text(function(d, i) {
return i + 1;
});
最终的结果是在每个g.item元素中追加了一个text元素,其text值为对应i的值
:
<g class="item" transform="translate(0, 0)">
<circle r="40" />
<text>1</text>
</g>
<g class="item" transform="translate(120, 0)">
<circle r="40" />
<text>2</text>
</g>
<g class="item" transform="translate(240, 0)">
<circle r="40" />
<text>3</text>
</g>
.remove
删除selection中的元素:
d3.selectAll('circle')
.remove();
Chaining
绝大部分selection的方法都仍然返回selection本身,因此这意味着我们可以链式方法继续调用其他的方法:
d3.selectAll('circle')
.style('fill', 'orange')
.attr('r', 20)
.on('click', function(d, i) {
d3.select('.status')
.text('You clicked on circle ' + i);
});
Each and call
.each
允许对每个selection中的每个元素执行一段函数功能(同样传入d,i, this参数可以使用), .call
则允许对 selection itself. 执行相应的函数功能
看下面的例子:
function addNumberedCircle(d, i) { d3.select(this) .append('circle') .attr('r', 40); d3.select(this) .append('text') .text(i + 1) .attr('y', 50) .attr('x', 30); } d3.selectAll('g.item') .each(addNumberedCircle);
再看一个.each
调用的例子
d3.selectAll('circle') .each(function(d, i) { var odd = i % 2 === 1; d3.select(this) .style('fill', odd ? 'orange' : '#ddd') // 对奇偶元素使用不用的颜色和半径大小 .attr('r', odd ? 40 : 20); });
而.call则传入selection本身,这实际上是javascript本身的特性!
function addNumberedCircle(selection) { selection .append('circle') .attr('r', 40); selection .append('text') .text(function(d, i) { return i + 1; }) .attr('y', 50) .attr('x', 30); } d3.selectAll('g.item') .call(addNumberedCircle);
Filtering and sorting selections
We can filter a selection using .filter
. A function is usually passed into .filter
which returns true
if the element should be included. .filter
returns the filtered selection.
我们也可以使用selection的.filter方法来过滤对应selection中的元素。通常我们通过传入一个函数(该函数同样具有d,i,this可以访问)给到.filter方法来告诉d3,如果该函数返回true则该i元素将被包含住,如果返回false,则不被包含在filter的返回selection中,而.filter则返回filtered selection.
比如下面的例子中我们对偶数的元素来着色为orange色,奇数元素作色为蓝色:
d3.selectAll('circle') .filter(function(d, i) { // filter返回偶数的元素 return i % 2 === 0; }) .style('fill', 'orange');// 偶数元素作色为橘色 d3.selectAll('circle') .filter(function(d, i) { // filter返回奇数的元素 return i % 2 === 1; }) .style('fill', 'blue'); // 奇数元素作色为蓝色
.sort()排序只有在有joined数据时才有意义。我们可以传入一个比较函数来调用.sort对selection中的元素执行排序操作。比较函数本身有两个参数,通常为a,b分别代表了被比较的两个元素上绑定的datam.如果比较函数返回负值,那么a将在b之前,如果是正,则a放在b之后。
下面假定selection上有以下绑定的数据:
[ {"name":"Andy","score":37}, {"name":"Beth","score":39}, {"name":"Craig","score":31}, {"name":"Diane","score":35}, {"name":"Evelyn","score":38} ]
d3.selectAll('.person') .sort(function(a, b) { return b.score - a.score; });
myData = [ { "name": "Andy", "score": 37 }, { "name": "Beth", "score": 39 }, { "name": "Craig", "score": 31 }, { "name": "Diane", "score": 35 }, { "name": "Evelyn", "score": 38 } ]; var barWidth = 400; var barScale = d3.scaleLinear().domain([0, 100]).range([0, barWidth]); var u = d3.select('#wrapper') .selectAll('.person') .data(myData); var entering = u.enter() .append('div') .classed('person', true); entering.append('div') .classed('label', true) .text(function(d) { return d.name; }); entering.append('div') .classed('bar', true) .style('width', function(d) { return barScale(d.score) + 'px'; }); function sort() { d3.selectAll('.person') .sort(function(a, b) { return b.score - a.score; }); }