23174


初めて投稿します。html/JavaScript劇的初心者です。  
今、ウェブサイトでd3jsを使って複数枚グラフを表示しようとしているのですが、なぜか後のグラフしか表示されません。  
なにかd3jsを使う上で気をつけないといけない点ありますでしょうか。

ソースコードを添付します。

HTML

[code]

    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>W2V execution</title>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
        <script src="http://d3js.org/topojson.v0.min.js"></script>
      </head>
      <body>
        <table>
          <tr>
            <td>
              <h1> KeyWord1： <?php echo $input_kw[0] ?> </h1>
              <div id="bubble"></div>
              <script src = "bubblechart.js"></script>
            </td>
            <td>
              <h1> KeyWord2： <?php echo $input_kw[1] ?> </h1>
              <div id="bubble2"></div> 
              <script src = "bubblechart2.js"></script>
            </td>
          </tr>
        </table>
      </body>
    </html>
    
[/code]

bubblechart.js

[code]

    var diameter = 350,
    format = d3.format(",d"),
    color = d3.scale.category20c();
    
    var bubble = d3.layout.pack()
      .sort(null)
      .size([diameter, diameter])
      .padding(1.5);
    
    var svg = d3.select("#bubble").append("svg")
      .attr("width", diameter)
      .attr("height", diameter)
      .attr("class", "bubble");
    
    d3.json("/data/kw1.txt", function(error, root) {
      var node = svg.selectAll(".node")
        .data(bubble.nodes(classes(root))
        .filter(function(d) { return !d.children; }))
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    
      node.append("title")
          .text(function(d) { return d.className + ": " + format(d.value); });
    
      node.append("circle")
          .attr("r", function(d) { return d.r; })
          .style("fill", function(d) { return color(d.packageName); });
    
      node.append("text")
          .attr("dy", ".3em")
          .style("text-anchor", "middle")
          .text(function(d) { return d.className.substring(0, d.r / 3); });
    });
    
    function classes(root) {
      var classes = [];
    
      function recurse(name, node) {
        if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
        // else classes.push({packageName: name, className: node.name, value: node.size});
        else classes.push({packageName: name, className: node.Keyword, value: node.Value});
      }
    
      recurse(null, root);
      return {children: classes};
    }
    
[/code]

bublechart2.js

[code]

    var diameter = 350,
    format = d3.format(",d"),
    color = d3.scale.category20c();
    
    var bubble = d3.layout.pack()
      .sort(null)
      .size([diameter, diameter])
      .padding(1.5);
    
    var svg = d3.select("#bubble2").append("svg")
      .attr("width", diameter)
      .attr("height", diameter)
      .attr("class", "bubble");
    
    d3.json("/data/kw2.txt", function(error, root) {
      var node2 = svg.selectAll(".node2")
        .data(bubble.nodes(classes(root))
        .filter(function(d) { return !d.children; }))
        .enter().append("g")
        .attr("class", "node2")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    
      node2.append("title")
           .text(function(d) { return d.className + ": " + format(d.value); });
    
      node2.append("circle")
           .attr("r", function(d) { return d.r; })
           .style("fill", function(d) { return color(d.packageName); });
    
      node2.append("text")
           .attr("dy", ".3em")
           .style("text-anchor", "middle")
           .text(function(d) { return d.className.substring(0, d.r / 3); });
    });
    
    function classes(root) {
      var classes = [];
    
      function recurse(name, node2) {
        if (node2.children) node2.children.forEach(function(child) { recurse(node2.name, child); });
        // else classes.push({packageName: name, className: node2.name, value: node2.size});
        else classes.push({packageName: name, className: node2.Keyword, value: node2.Value});
      }
    
      recurse(null, root);
      return {children: classes};
    }
    
[/code]


CC BY-SA 3.0
2016-03-15T10:30:19.813
23160
2016-03-15T18:14:20.867
2016-03-15T17:47:42.207
3068
14793
question
javascript php html d3.js
D3jsのグラフを1ページに複数枚表示できない


### 原因

jsファイルを2つに分けていますが、これらに定義されている変数や関数は、同じHTMLに読み込まれると同じスコープ(有効範囲)になります。  
(1つのjsファイルにまとめて書いたのと同じ状態になります)

そのため同じ名前の変数や関数があると、より後に宣言されている定義(内容)で上書きされます。

今回該当するのは下記のもので、これらがHTML上で後に読み込まれている`bublechart2.js`の定義で上書きされて、後のグラフしか表示されなくなっています。

変数：

  * `diameter`
  * `format`
  * `color`
  * `bubble`
  * `svg`

関数：

  * `function classes(root)`

**`svg`以外の変数**は、どちらも全く同じ値が代入されているため、(今のところ)上書きされてても動作に影響はないかと思います。

`function classes(root)`は、関数の処理の内容が全く同じなので、(今のところ)上書きされてても動作に影響はないかと思います。

### 修正方法

bublechart2.jsの`svg`を`svg2`等の別の名前にすることで解決できるかと思います。  
`d3.json("/data/kw2.txt", function(error, root) {...})`内の`svg`も一緒に変える必要があります。

修正後のbublechart2.js

[code]

    (前略)
    
    // `svg2`へ変更
    var svg2 = d3.select("#bubble2").append("svg")
      .attr("width", diameter)
      .attr("height", diameter)
      .attr("class", "bubble");
    
    d3.json("/data/kw2.txt", function(error, root) {
      // `svg2`へ変更
      var node2 = svg2.selectAll(".node2")
        .data(bubble.nodes(classes(root))
        .filter(function(d) { return !d.children; }))
        .enter().append("g")
        .attr("class", "node2")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    
      node2.append("title")
           .text(function(d) { return d.className + ": " + format(d.value); });
    
      node2.append("circle")
           .attr("r", function(d) { return d.r; })
           .style("fill", function(d) { return color(d.packageName); });
    
      node2.append("text")
           .attr("dy", ".3em")
           .style("text-anchor", "middle")
           .text(function(d) { return d.className.substring(0, d.r / 3); });
    });
    
    (後略)
    
[/code]

### 余談

JavaScriptのfunctionは新しいスコープを作ります。functionの外部で定義されている変数と同じ名前の変数をfunction内で宣言した場合、function内で定義した変数が参照されます。

[code]

    var a = 0;
    var svg = "svg";
    
    function first() {
      // トップレベルの`a`とは別の変数として扱われる
      var a = 1;
      console.log("first: " + a);
    
      // 外に定義されている`svg`が参照される
      console.log("first: " + svg);
    }
    
    function second() {
      // トップレベルの`a`やfirstの`a`とは別の変数として扱われる
      var a = 2;
      console.log("second: " + a);
    }
    
    // ブラウザの開発者ツールのConsoleに1とsvgが出力される
    first();
    
    // 2が出力される
    second();
    
    // 0が出力される
    console.log("toplevel: " + a);
    
[/code]

つまり、今回のソースコードで`d3.json("...", function(error, root) {...})`のfunction内でそれぞれ`var
node`、`var
node2`と別々の名前が付けられていますが、それぞれ別のfunctionなので同じ名前を付けていても別の変数として扱われ、意図した通りに動作します。


CC BY-SA 3.0
2016-03-15T16:57:20.203
23174
2016-03-15T18:14:20.867
2016-03-15T18:14:20.867
3068
3068
23160
answer