d3 の enter()
などの意味や selection の中身がよくわからなかったので調べてみました。それにしても d3 のドキュメントの充実っぷりは半端ないですね。
selection とは
d3 の全ての基本が d3.selection です。基本的には DOM 要素の入った配列(の入った配列)ですが、data()
, append()
などのメソッド(d3 では operator と呼ばれている)がついており、DOM 要素に対していろいろな操作をすることができます。
<body>
<p>Hello, World!</p>
<p>Hello, World!</p>
<p>Hello, World!</p>
</body>
var p = d3.select('body').selectAll('p');
console.log(p instanceof d3.selection);
// true
for (var k in p) if (p.hasOwnProperty(k)) {
console.log(k);
}
// 0
for (var k in p[0]) if (p[0].hasOwnProperty(k)) {
console.log(k, p[0][k]);
}
// 0 <p>Hello, World!</p>
// 1 <p>Hello, World!</p>
// 2 <p>Hello, World!</p>
// parentNode <body>…</body>
selection のネスト
selectAll()
を一回呼んだだけだと、なぜか一個だけ配列の入った配列です。入れ子状の DOM に対して selectAll()
を二度呼ぶと一階層繰り上がり、一番上の配列の要素が複数になります。
<table>
<tr><th>Wow</th><td>Hello</td></tr>
<tr><th>Wow</th><td>Hello</td></tr>
<tr><th>Wow</th><td>Hello</td></tr>
</table>
console.log(d3.selectAll('tr'));
// [Array[3]]
console.log(d3.selectAll('tr').selectAll('td'));
// [Array[1], Array[1], Array[1]]
data() と selection join
data()
を使うと selection にデータを関連づけることができます。具体的には、各 DOM 要素に __data__
プロパティが設定されます。また、その際 selection から update, enter, exit という三種類の subselection(?)が生まれます。
- update selection は、要素とデータ両方揃った分の配列。
data()
の返り値はこれです。要素を更新するのに使います。
enter()
とexit()
メソッドがついており、それぞれ下の二つを返します。 - enter selection は、要素よりもデータの方が多かった場合に余った分のデータ(を格納したオブジェクト)の入った配列。要素を新しく追加するのに使います。
- exit selection は、データよりも要素の方が多かった場合に余った分の要素の入った配列。不要になった要素を削除するのに使います。
var updating = d3.select('body').selectAll('p')
.data([4, 8, 15, 16, 23, 42]);
console.log(updating instanceof d3.selection);
// true
for (var k in updating) if (updating.hasOwnProperty(k)) {
console.log(k);
}
// -> 0
// -> enter
// -> exit
for (var k in updating[0]) if (updating[0].hasOwnProperty(k)) {
console.log(k, updated[0][k]);
}
// 0 <p>Hello, World!</p>
// 1 <p>Hello, World!</p>
// 2 <p>Hello, World!</p>
// 3 null
// 4 null
// 5 null
// parentNode <body>…</body>
enter selection はこんな感じです。
var entering = updating.enter();
console.log(entering instanceof d3.selection);
// false
for (var k in entering) if (entering.hasOwnProperty(k)) {
console.log(k);
}
// 0
for (var k in entering[0]) if (entering[0].hasOwnProperty(k)) {
console.log(k, entering[0][k]);
}
// 0 null
// 1 null
// 2 null
// 3 Object
// 4 Object
// 5 Object
// update [<p>Hello, World!</p>, <p>Hello, World!</p>, <p>Hello, World!</p>, null, null, null]
// parentNode <body screen_capture_injected="true">…</body>
なお entering[0][3,4,5]
に入っている Object
は __data__
プロパティにデータを格納しています。
exit selection は enter selection と同じような感じなので省略します。
一見面倒な仕組みですが、大きなメリットがあります。
- 変更がある度に全てを書き換えるのではなく、必要な分だけ書き換えるので、パフォーマンスの向上が期待できる
- 要素の追加や削除にアニメーションをつけられる