d3js enter/exit深入了解

Posted 世有因果知因求果

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了d3js enter/exit深入了解相关的知识,希望对你有一定的参考价值。

在 Data joins 章节我们演示了当data和dom element个数相同时的情况

<div id="content">
  <div></div>
  <div></div>
  <div></div>
</div>

给定下面的数据

var myData = [ 10, 40, 20 ];

当我们对div元素join上数据时

d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData);

myData 数据的个数和div元素的个数是相同的。

但是,如果数据和dom元素的个数如果不相同的话,会怎样呢?

  • 如果数据个数多于dom元素个数那么说明有DOM元素的缺口,我们需要 add elements
  • 如果数据个数少于dom元素个数,那么说明DOM元素有剩余,我们需要 remove elements

幸运的是D3可以帮助我们非常方便地管理这两种情况,分别通过 .enter来添加 而通过.exit来删除.

.enter

.enter 指明了所有和data相比dom元素的缺口,enter和exit都是定义在update selection上的 (the selection returned by .data):

d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData)
  .enter();

.enter 返回代表需要增加的那些元素(已经绑定了数据)的selection. 通常跟随.enter的都是 .append 操作

d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData)
  .enter()
  .append(‘div‘);

Let’s look at an example. Suppose we have the following div elements:

<div id="content">
  <div></div>
  <div></div>
  <div></div>
</div>

and this data:

var myData = [‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘];

we use .enter and .append to add div elements for D and E:

  d3.select(‘#content‘)
    .selectAll(‘div‘)
    .data(myData)
    .enter()
    .append(‘div‘);

Note that we can join an array to an empty selection which is a very common pattern in the examples on the D3 website.

.exit

.exit returns an exit selection which consists of the elements that need to be removed from the DOM. It’s usually followed by .remove:

d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData)
  .exit()
  .remove();

Let’s repeat the example above, but using .exit. Starting with elements:

<div id="content">
  <div></div>
  <div></div>
  <div></div>
</div>

and data (notice that it’s shorter than the selection):

var myData = [‘A‘];

we use .exit and .remove to remove the surplus elements:

d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData)
  .exit()
  .remove();

Putting it all together

So far in this section we’ve not concerned ourselves with modifying elements using functions such as .style, .attr and .classed.

D3 allows us to be specific about which elements are modified when new elements are entering. We can modify:

  • the existing elements
  • the entering elements
  • both existing and entering elements

(Most of the time the last option is sufficient, but sometimes we might want to style entering elements differently.)

The existing elements are represented by the update selection. This is the selection returned by .data and is assigned to u in this example:

var myData = [‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘];

var u = d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData);

u.enter()
  .append(‘div‘);

u.text(function(d) {
  return d;
});

When the button is clicked, new elements are added, but because .text is only called on the update selection, it’s only the existing elements that are modified. (Note that if the button is clicked a second time, all the elements are modified. This is because the selection will contain all 5 div elements. Don’t worry about this too much if you’re new here!)

The entering elements are represented by the enter selection. This is the selection returned by .enter. We can modify the enter selection using:

var myData = [‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘];

var u = d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData);

u.enter()
  .append(‘div‘)
  .text(function(d) {
    return d;
  });

When the button is clicked, new elements are added and their text content is updated. Only the entering elements have their text updated because we call .text on the enter selection.

If we want to modify the existing and entering elements we could call .text on the update and enter selections.

However D3 has a function .merge which can merge selections together. This means we can do the following:

var myData = [‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘];

var u = d3.select(‘#content‘)
  .selectAll(‘div‘)
  .data(myData);

u.enter()
  .append(‘div‘)
  .merge(u)
  .text(function(d) {
    return d;
  });
This is a big departure from v3. The entering elements were implicitly included in the update selection so there was no need for .merge.

General update pattern

A common pattern (proposed by D3’s creator Mike Bostock) is to encapsulate the above behaviour of adding, removing and updating DOM elements in a single function:

function update(data) {
  var u = d3.select(‘#content‘)
    .selectAll(‘div‘)
    .data(data);

  u.enter()
    .append(‘div‘)
    .merge(u)
    .text(function(d) {
      return d;
    });

  u.exit().remove();
}

Typically the update function is called whenever the data changes.

Here’s another example where we colour entering elements orange:

function update(data) {
  var u = d3.select(‘#content‘)
    .selectAll(‘div‘)
    .data(data);

  u.enter()
    .append(‘div‘)
    .classed(‘new‘, true)
    .text(function(d) {
      return d;
    });

  u.text(function(d) {
      return d;
    })
    .classed(‘new‘, false);

  u.exit().remove();
}

Data join key function

When we do a data join D3 binds the first array element to the first element in the selection, the second array element to the second element in the selection and so on.

However, if the order of array elements changes (such as during element sorting, insertion or removal), the array elements might get joined to different DOM elements.

We can solve this problem by providing .data with a key function. This function should return a unique id value for each array element, allowing D3 to make sure each array element stays joined to the same DOM element.

Let’s look at an example, first using a key function, and then without.

We start with an array [‘Z‘] and each time the button is clicked a new letter is added at the start of the array.

Because of the key function each letter will stay bound to the same DOM element meaning that when a new letter is inserted each existing letter transitions into a new position:

Without a key function the DOM elements’ text is updated (rather than position) meaning we lose a meaningful transition effect:

There’s many instances when key functions are not required but if there’s any chance that your data elements can change position (e.g. through insertion or sorting) and you’re using transitions then you should probably use them.

以上是关于d3js enter/exit深入了解的主要内容,如果未能解决你的问题,请参考以下文章

enter/exit 和 popEnter/popExit 动画有啥区别?

d3js layout 深入理解

d3js scales深入理解

d3js data joins深入理解

D3.js 入门系列 --- 7 理解 update, enter, exit 的使用

内置方法 call enter exit