d3.jsのサンプルコードを見ると、selectAll(), data(), enter(), append()というシーケンスが多用されている。これらの関係や挙動が、初心者にはわかりにくい。そこで、実際の挙動を見ながら、理解してみる。
参考:knowledge stockpile: Understanding selectAll, data, enter, append sequence in D3.js
【例1】 下のスクリプトをブラウザで表示すると…
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Creating paragraphs dynamically from data</title> <script type="text/javascript" src="../d3/d3.v3.js"></script> </head> <body> <script type="text/javascript"> var dataset = [ 5, 10, 15, 20, 25 ]; d3.select("body").selectAll("p") .data(dataset) .enter() .append("p") .text(function(d){return "data is "+d;}); </script> </body> </html>
...次のようになる。
【例2】あらかじめ'p'タグ要素が存在する場合を見る。下の例では、pタグブロックを2つ作った。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Creating paragraphs dynamically from data</title> <script type="text/javascript" src="../d3/d3.v3.js"></script> </head> <body> <div id="ex1"> <p>line 1</p> <p>line 2</p> </div> <script type="text/javascript"> var dataset = [ 5, 10, 15, 20, 25 ]; d3.select("body").selectAll("p") .data(dataset) .enter() .append("p") .text(function(d){return "data is "+d;}); </script> </body> </html>
この表示結果は次の通り。既存のpタグブロックの数に相当するデータは無視される。
この挙動は、次のように理解される。
- d3.select("body").selectAll("p")は、bodyタグ内のすべてのp要素を取り出す。取り出したp要素には、デフォルトキー、0,1,2,...がアサインされる。
- .data(dataset)は、データセット(dataset)の各要素を順に取り出す「ループ」になるとともに、キーをアサインする。デフォルトのキーはやはり、0,1,2,3,... となる。
- .enter()以下は、data「ループ」の現在のキーに相当する要素が存在ない場合のみ、実行される。
つまり、
【例1】では、既存p要素は存在しないため、datasetのすべての要素に対して.enter()以下が実行される
【例2】では、2つのp要素が存在し、これらのキーは0と1である。このため、datasetのキー0,1、すなわち最初の2つの要素に対して.enter()以下は実行されない。キー2,3,4の要素のみ.enter()以下が実行される
と言うことになる。
.dataset()のキーを明示的に変更するには、第2引数で指定する。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Creating paragraphs dynamically from data</title> <script type="text/javascript" src="../d3/d3.v3.js"></script> </head> <body> <div id="ex1"> <p>line 1</p> <p>line 2</p> </div> <script type="text/javascript"> var dataset = [ 5, 10, 15, 20, 25 ]; d3.select("body").selectAll("p") .data(dataset,function(d){return d+2;}) .enter() .append("p") .text(function(d){return "data is "+d;}); </script> </body> </html>
上のスクリプトの表示結果は次の通り。pタグ要素が存在していても、'd+2'によって.data(dataset)「ループ」のキーは、2,3,4,... となり、既存のpタグ要素のキー(0,1)と重複しないため、全てdataset要素に対してenter()以下が実行されることになる。
【例3】ちなみに、.selectAll("p")を外すとどうなるか実験してみた。
<body> <script type="text/javascript"> var dataset = [ 5, 10, 15, 20, 25 ]; d3.select("body") .data(dataset) .enter() .append("p") .text(function(d){return "data is "+d;}); </script>
表示結果は次の通り、データ配列の一番目の要素が表示されない。
これは、.select("body")のキー、0が有効になっていると思われる。このため、.data(dataset)の最初の要素に対しては.enter()以下が実行されないのだろう。