d3.js-存在しない要素を返すselectAllの目的は何ですか[重複]

Aug 24 2020

d3.jsを使用すると、データセットに基づいて長方形を追加する次の簡単なコードがあります。

私はsvgもrectも持っていないので、.append()を介して追加しています。

            let dataset = [ 1, 2, 3, 4, 5 ]
            let svg = d3.select("body")
                        .append("svg")
                        .attr /* width, height */

            svg.selectAll("rect")
               .data(dataset)
               .enter()
               .append("rect")
               .attr /* x, y, width, height, etc */

これは完全に機能します。

私が質問しているのはselectAll("rect")です。

なぜこれが必要なのですか?以下のように削除すると、rect要素がbody要素の外側に移動します。

しかし、svgの追加は、参照された要素の最後に追加することを想定していませんでしたか?(この場合はsvg?)

            svg.data(dataset)
               .enter()
               .append("rect")
               .attr /* x, y, width, height, etc */

selectAllが参照を返すことは知っています。

要素が存在しない場合、特別な参照を返しますか?

回答

1 HugoElhaj-Lahsen Aug 24 2020 at 02:15

D3は、データ結合と呼ばれる更新のパラダイムを使用します。グラフの使用法に合わせて、データの増分更新用に最適化されています。

データ結合を使用するには、「選択」を操作します。実行したいことを選択で表す代わりに、データをどのように変換するかを表します。

let dataset = [ {name: "a", value: 1}, {name: "b", value: 2}, {name: "c", value: 3} ]
let svg = d3.select("body")
  .append("svg")

const selection = svg.selectAll("rect")
  .data(dataset, d => d.name) // data join, each "rect" will correspond to an data
// second argument is a key function to uniquely identify each data

selection
  .enter()
  .append("rect")
  /* attr: x, y, height, width */

選択はデータ結合を表します。ここで、選択を行っていることをD3に伝えます。ただし、現時点ではDOMには何も存在しないため、空の選択が取得されます。

選択を定義するとき:

  • DOMには「rect」ノードがありません
  • しかし、データがあります

別の見方をすると:

         data
 data  &nodes  nodes
       +-----+
+--------------------+
|      |     |       |
|  a:1 |     |       |
|  b:2 |     |       |
|  c:3 |     |       |
|      |     |       |
+--------------------+
       +-----+

enter選択はありませんノードとデータが含まれています。つまり、これは、Enterを使用するときに操作するデータです。

ここでデータセットを変更するとします。DOMノードを更新するにはどうすればよいですか?

dataset =  [ {name: "a", value: 9}, {name: "c", value: 3}, {name: "d", value: 4} ]
// note how we need to select rect to get the existing data.
// this is the "update" selection
const selection = svg.selectAll("rect")
  .data(dataset, d => d.name)
  .attr("x", d => d.value) 

// the "enter" selection
selection.enter()
  .append("rect")

// the "exit" selection
selection.exit().remove()

私たちが今持っているのは3つの選択肢です。もう一度図を描くと、データは次のようになります。

         data
 data  &nodes  nodes
       +-----+
+--------------------+
|      |     |       |
| d:4  | a:9 |  b:2  |
|      |     |       |
|      |     |       |
|      |     |       |
+--------------------+
       +-----+

データ結合を行うとどうなりますか?データが変更されました。今svg.selectAll("rect")すでに存在する選択を取る:D3はDOM内に保存されています。これが、CSSセレクターをに渡す必要がある理由ですselectAllD3はそのセレクターを使用して.__data__、各DOMノードの属性を介してDOMに保存されている既存の選択を検索します。

現在、新しいデータを観察しており、それをDOMにあるものと比較しています。

  • まだデータがあり、前のデータ結合のノードがあります。aがまだ存在することはわかっていますが、データが{name、value}のオブジェクトであるため、その値が変更されています。つまり、「データとノード」にあります。
  • bはdomにノードを持っていますが、対応する新しいデータには存在しません。それは「ノード」にあります。
  • dは新しいアイテムです。ノードのないデータです。つまり、「データ」に入ります。

cが更新されなかったため、どの選択にも含まれていません

グラフの名前を変更しましょう。

 enter  update  exit
       +-----+
+--------------------+
|      |     |       |
| d:4  | a:9 |  b:2  |
|      |     |       |
|      |     |       |
|      |     |       |
+--------------------+
       +-----+

選択範囲を直接使用する場合(選択範囲の更新)、call enter()、またはcallを使用するとexit()、常にこれら3つの選択範囲のいずれかに取り組んでいます。特に大規模なデータの場合は、必要なときにのみ更新を管理するか、毎回ノードを再作成せず、必要なときにのみ破棄する方がはるかに効率的です。

enter()トランジションのみを使用する場合でも、新しいアイテムのみが作成されるようにすることで、D3の効率が向上します。

アイデアは、3つの状態すべてを管理するスニペットをrender()関数のようなものに配置し、それを常に呼び出すことができるということです。最初にグラフを生成するときとデータを更新するときです。D3は、すべてのデータ簿記を管理します。