水戸地図(β)

2017年08月29日

D3.jsでアロー関数を使う時の注意点

既存のD3.jsのコードを アロー関数 で置き換える際に変な動作を起こしてしまったのでメモしておきます。

追記 (2020/08/29)

2020年8月にリリースされた D3 v6 ではイベントリスナの書き方が変わりました。
詳しくは D3 v6 アロー関数使用時の移行ガイド へ。以下は v5 以前のコードになります。

D3.jsでは、DOMにイベントを与える selection.on(typenames, listener) で、イベントリスナ内でそのDOMを選択したいときにthisを使うことが多いです。

例えば、以下のようなコードがよく使われます。

  d3.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", function(d) {return xScale(0);})
    .attr("y", function(d) {return yScale(d.name);})
    .attr("width", function(d) {return xScale(d.value) - xScale(0);})
    .attr("height", yScale.bandwidth())
    .attr("fill", "white")
    .on("mouseover", function() {
      d3.select(this)
        .transition().duration(500)
        .attr("fill", "orange");
    })
    .on("mouseout", function() {
      d3.select(this)
        .transition().duration(500)
        .attr("fill", "white");
    });

このコードに使われている関数を アロー関数 で置き換えると、最後の二つ.on("mouseover", listener).on("mouseout", listener)で設定されたイベントリスナーは正常に動作しません。これはアロー関数のthisを束縛しないという性質によるものです。

アロー関数に置き換えつつ、今まで通りの動作を与えるには、以下のようにlistenerの引数にd, i, nodesを取り、thisと書いていたところをnodes[i]と書くことで解決します。

  d3.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", d => xScale(0))
    .attr("y", d => yScale(d.name))
    .attr("width", d => xScale(d.value) - xScale(0))
    .attr("height", yScale.bandwidth())
    .attr("fill", "white")
    .on("mouseover", (d, i, nodes) => {
      d3.select(nodes[i])
        .transition().duration(500)
        .attr("fill", "orange");
    })
    .on("mouseout", (d, i, nodes) => {
      d3.select(nodes[i])
        .transition().duration(500)
        .attr("fill", "white");
    });

selection.on(typenames, listener) ではイベントリスナーの引数に、datum(d)、index(i)、nodesが渡されます。

  • datum (d): 要素にバインドされているデータ(datumはdataの単数形)
  • index (i): セレクションの中の順番
  • nodes: NodeList(要素の集合)
  d3.selectAll("rect")
    .on("click", (d, i, nodes) => {
      console.log(d)
      //=> datum (= data[i])

      console.log(i);
      //=> index (0, 1, 2, ...)

      console.log(nodes);
      //=> NodeList [rect, rect, rect, ...]
      // d3.selectAll("rect").nodes()で得られるものと同じ

      console.log(nodes[i]);
      //=> rect
    });

既存のコードを無理にアロー関数に置き換える必要はありませんが、置き換えたときにこういうことが起きる可能性はあると思うので、備忘録として残しておきます。

広告

2017年08月29日 最終更新日2020年08月29日

D3.jsでアロー関数を使う時の注意点

技術記事

Top

水戸地図(β)