質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

D3.jsにおいて、開閉するtreeグラフを複数表示する方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,700

wi-fi

score 10

 前提・実現したいこと

JavaScript(D3.js)でこちらのURL(D3.js v4/v5 treeを開閉する方法 – サンプル)を参考にして、開閉するグラフを作成しています。
このグラフを同じページに複数作成したいのです。
下記のソースコードにより、複数表示自体はできるのですが、開閉する機能を全てにつけることができません。
一番下に描画されたグラフには、開閉機能が付いているのですが、それ以外のグラフは、ノードをクリックしても動きません。
これを、全ての木に対して、開閉するグラフにしようとしているのですが、どのように作成すればよいでしょうか。
当方D3.js初心者ですよろしくお願いいたします。

 該当のソースコード

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <title>D3 collapsible tree in v4/v5</title>
    <!-- 1. スタイルの設定 -->
    <style>
     .node {
       cursor: pointer;
     }

     .node circle {
       fill: #fff;
       stroke: steelblue;
       stroke-width: 1.5px;
     }

     .link {
       fill: none;
       stroke: #555;
       stroke-opacity: 0.6;
       stroke-width: 1.5px;
     }
    </style>
  </head>

  <body>
    <svg id="tree0" width="800" height="600"></svg>
    <svg id="tree1" width="800" height="600"></svg>
    <svg id="tree2" width="800" height="600"></svg>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>
     for(var j=0;j<3;j++){
       // 2. 描画用データの準備
       var width = document.querySelector(`#tree${j}`).clientWidth;
       var height = document.querySelector(`#tree${j}`).clientHeight;
       var data = {
         "name": "A",
         "children": [
           { "name": "B" },
           {
             "name": "C",
             "children": [{ "name": "D" }, { "name": "E" }, { "name": "F" }]
           },
           { "name": "G" },
           {
             "name": "H",
             "children": [{ "name": "I" }, { "name": "J" }]
           },
           { "name": "K" }
         ]
       };

       // 3. 描画用データの変換
       root = d3.hierarchy(data);
       root.x0 = height / 2;
       root.y0 = 0;

       var tree = d3.tree().size([height, width - 160]);

       // 4. svgデータの描画用データの変換
       g = d3.select(`#tree${j}`).append("g").attr("transform", "translate(80,0)");
       update(root);
     }

     // 5. クリック時の呼び出し関数
     function toggle(d) {
       if(d.children) {
         d._children = d.children;
         d.children = null;
       } else {
         d.children = d._children;
         d._children = null;
       }
     }

     // 6.svg要素の更新関数
     var i = 0;
     function update(source) {

       // tree レイアウト位置を計算
       tree(root);

       // 子、孫方向の位置設定
       root.each(function(d) { d.y = d.depth * 320; });

       // ノードデータをsvg要素に設定
       var node = g.selectAll('.node')
                   .data(root.descendants(), function(d) { return d.id || (d.id = ++i); });

       // ノード enter領域の設定
       var nodeEnter = node
         .enter()
         .append("g")
         .attr("class", "node")
         .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
         .on("click", function(d) {
           toggle(d);
           update(d);
         });

       nodeEnter.append("circle")
                .attr("r", 5)
                .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

       nodeEnter.append("text")
                .attr("x", function(d) { return d.children || d._children ? -13 : 13; })
                .attr("dy", "3")
                .attr("font-size", "150%")
                .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
                .text(function(d) { return d.data.name; })
                .style("fill-opacity", 1e-6);

       // ノード enter+update領域の設定
       var nodeUpdate = nodeEnter.merge(node);
       var duration = 500;

       nodeUpdate.transition()
                 .duration(duration)
                 .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

       nodeUpdate.select("circle")
                 .attr("r", 8)
                 .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

       nodeUpdate.select("text")
                 .style("fill-opacity", 1);

       // ノード exit領域の設定
       var nodeExit = node
         .exit()
         .transition()
         .duration(duration)
         .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
         .remove();

       nodeExit.select("circle")
               .attr("r", 1e-6);

       nodeExit.select("text")
               .style("fill-opacity", 1e-6);

       // リンクデータをsvg要素に設定
       var link = g.selectAll(".link")
                   .data(root.links(), function(d) { return d.target.id; });

       // リンク enter領域のsvg要素定義
       var linkEnter = link.enter().insert('path', "g")
                           .attr("class", "link")
                           .attr("d", d3.linkHorizontal()
                                        .x(function(d) { return source.y0; })
                                        .y(function(d) { return source.x0; }));

       // リンク enter+update領域の設定
       var linkUpdate = linkEnter.merge(link);
       linkUpdate
         .transition()
         .duration(duration)
         .attr("d", d3.linkHorizontal()
                      .x(function(d) { return d.y; })
                      .y(function(d) { return d.x; }));

       // リンク exit領域の設定
       link
         .exit()
         .transition()
         .duration(duration)
         .attr("d", d3.linkHorizontal()
                      .x(function(d) { return source.y; })
                      .y(function(d) { return source.x; })
         )
         .remove();

       // 次の動作のために現在位置を記憶
       node.each(function(d) {
         d.x0 = d.x;
         d.y0 = d.y;
       });
     }

    </script>
  </body>

</html>

 試したこと

rootやgといった変数を自分で新たに設定したり、update関数の中の変数を全てグローバルスコープにしたりと色々試したのですが実現せず・・・
どうやら、 (///ノードデータをsvg要素に設定) とコメントアウトに書いてる部分 のidの設定が上手くいってない模様・・・
それ以外にも原因はあると思いますが。

 補足情報(FW/ツールのバージョンなど)

D3.js v5
ブラウザはChromeを使用しています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

update関数でグローバル変数使ってるせいですね
ちょっと強引ですが(多分このデータでしか動かない気もしますが)

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <title>D3 collapsible tree in v4/v5</title>
    <!-- 1. スタイルの設定 -->
    <style>
     .node {
       cursor: pointer;
     }

     .node circle {
       fill: #fff;
       stroke: steelblue;
       stroke-width: 1.5px;
     }

     .link {
       fill: none;
       stroke: #555;
       stroke-opacity: 0.6;
       stroke-width: 1.5px;
     }
    </style>
  </head>

  <body>
    <svg id="tree0" width="800" height="600"></svg>
    <svg id="tree1" width="800" height="600"></svg>
    <svg id="tree2" width="800" height="600"></svg>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>
     for(var j=0;j<3;j++){
       // 2. 描画用データの準備
       var width = document.querySelector(`#tree${j}`).clientWidth;
       var height = document.querySelector(`#tree${j}`).clientHeight;
       var data = {
         "name": "A",
         "children": [
           { "name": "B" },
           {
             "name": "C",
             "children": [{ "name": "D" }, { "name": "E" }, { "name": "F" }]
           },
           { "name": "G" },
           {
             "name": "H",
             "children": [{ "name": "I" }, { "name": "J" }]
           },
           { "name": "K" }
         ]
       };

       // 3. 描画用データの変換
       var root = d3.hierarchy(data);
       root.x0 = height / 2;
       root.y0 = 0;

       root.root = root;

       root.tree = d3.tree().size([height, width - 160]);

       // 4. svgデータの描画用データの変換
       root.g = d3.select(`#tree${j}`).append("g").attr("transform", "translate(80,0)");
       update(root, root, root.g, root.tree);
     }

     // 5. クリック時の呼び出し関数
     function toggle(d) {
       if(d.children) {
         d._children = d.children;
         d.children = null;
       } else {
         d.children = d._children;
         d._children = null;
       }
     }

     // 6.svg要素の更新関数
     var i = 0;
     function update(source, root, g, tree) {
       // tree レイアウト位置を計算
       tree(root);

       // 子、孫方向の位置設定
       root.each(function(d) { d.y = d.depth * 320; });

       // ノードデータをsvg要素に設定
       var node = g.selectAll('.node')
                   .data(root.descendants(), function(d) { return d.id || (d.id = ++i); });

       // ノード enter領域の設定
       var nodeEnter = node
         .enter()
         .append("g")
         .attr("class", "node")
         .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
         .on("click", function(d) {
           toggle(d);
           update(d,
                  d.parent ? d.parent.root : d.root,
                  d.parent ? d.parent.g : d.g,
                  d.parent ? d.parent.tree : d.tree
                  );
         });

       nodeEnter.append("circle")
                .attr("r", 5)
                .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

       nodeEnter.append("text")
                .attr("x", function(d) { return d.children || d._children ? -13 : 13; })
                .attr("dy", "3")
                .attr("font-size", "150%")
                .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
                .text(function(d) { return d.data.name; })
                .style("fill-opacity", 1e-6);

       // ノード enter+update領域の設定
       var nodeUpdate = nodeEnter.merge(node);
       var duration = 500;

       nodeUpdate.transition()
                 .duration(duration)
                 .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

       nodeUpdate.select("circle")
                 .attr("r", 8)
                 .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

       nodeUpdate.select("text")
                 .style("fill-opacity", 1);

       // ノード exit領域の設定
       var nodeExit = node
         .exit()
         .transition()
         .duration(duration)
         .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
         .remove();

       nodeExit.select("circle")
               .attr("r", 1e-6);

       nodeExit.select("text")
               .style("fill-opacity", 1e-6);

       // リンクデータをsvg要素に設定
       var link = g.selectAll(".link")
                   .data(root.links(), function(d) { return d.target.id; });

       // リンク enter領域のsvg要素定義
       var linkEnter = link.enter().insert('path', "g")
                           .attr("class", "link")
                           .attr("d", d3.linkHorizontal()
                                        .x(function(d) { return source.y0; })
                                        .y(function(d) { return source.x0; }));

       // リンク enter+update領域の設定
       var linkUpdate = linkEnter.merge(link);
       linkUpdate
         .transition()
         .duration(duration)
         .attr("d", d3.linkHorizontal()
                      .x(function(d) { return d.y; })
                      .y(function(d) { return d.x; }));

       // リンク exit領域の設定
       link
         .exit()
         .transition()
         .duration(duration)
         .attr("d", d3.linkHorizontal()
                      .x(function(d) { return source.y; })
                      .y(function(d) { return source.x; })
         )
         .remove();

       // 次の動作のために現在位置を記憶
       node.each(function(d) {
         d.x0 = d.x;
         d.y0 = d.y;
       });
     }

    </script>
  </body>

</html>

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/17 17:57

    解答いただいた方法で、現在作っているWEBページにて正常に動かすことができました。
    困っていたので、本当に助かりました。ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る