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的以下函数:

NameBehaviourExample
.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 nameDescription
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;
    });
}

posted @ 2017-07-16 16:09  世有因果知因求果  阅读(3310)  评论(0编辑  收藏  举报