简体   繁体   English

在d3中更新图形

[英]Updating graph in d3

I'm working on creating a graph that updates when a button is clicked, however when clicking the button, it seems only the axis are updating, and not the data itself. 我正在创建一个图形,该图形在单击按钮时会更新,但是在单击按钮时,似乎只有轴在更新,而不是数据本身。

The current version is in this plunker, I've also attached the code below: http://plnkr.co/edit/85H6i25YPbTB0MRKtpZn?p=preview 当前版本位于该插件中,我还附加了以下代码: http ://plnkr.co/edit/85H6i25YPbTB0MRKtpZn?p=preview

I'm still quite new to D3 and have used aa few books and a lot of reading to get me to an ok level, but am struggling to find an answer to this specific question after trawling through many pages of the internet. 我对D3还是很陌生,已经使用了几本书和大量阅读来使我达到一个不错的水平,但是在浏览了许多互联网页面之后,我一直在努力寻找这个特定问题的答案。

It would be amazing if anyone could give me some guidance on where I'm going wrong. 如果有人能给我一些有关我出问题的指导,那将是惊人的。

<body>
      <svg width="960" height="500"></svg>
      <div id="option">
        <input name="updateButton" type="button" value="Click here to update the chart with results after the snap election" onclick="updateData()" />
      </div>

    <script type="text/javascript">
    //graph 1

    var svg = d3.select("svg"),
      margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 40
      },
      width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom,
      g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var x0 = d3.scaleBand()
      .rangeRound([0, width])
      .paddingInner(0.1);

    var x1 = d3.scaleBand()
      .padding(0.05);

    var y = d3.scaleLinear()
      .rangeRound([height, 0]);

    var z = d3.scaleOrdinal()
      .range(["#0087dc", "#d50000", "#FDBB30"]);

    d3.csv("data.csv", function(d, i, columns) {
      for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
      return d;
    }, function(error, data) {
      if (error) throw error;

      var keys = data.columns.slice(1);

      x0.domain(data.map(function(d) {
        return d.Year;
      }));
      x1.domain(keys).rangeRound([0, x0.bandwidth()]);
      y.domain([0, d3.max(data, function(d) {
        return d3.max(keys, function(key) {
          return d[key];
        });
      })]).nice();

      g.append("g")
        .selectAll("g")
        .data(data)

      .enter().append("g")
        .attr("transform", function(d) {
          return "translate(" + x0(d.Year) + ",0)";
        })
        .selectAll("rect")
        .data(function(d) {
          return keys.map(function(key) {
            return {
              key: key,
              value: d[key]
            };
          });
        })

      .enter().append("rect")
        .attr("x", function(d) {
          return x1(d.key);
        })
        .attr("y", function(d) {
          return y(d.value);
        })
        .attr("width", x1.bandwidth())
        .attr("height", function(d) {
          return height - y(d.value);
        })
        .attr("fill", function(d) {
          return z(d.key);
        });



      g.append("g")
        .attr("class", "axis")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x0));

      g.append("g")
        .attr("class", "axis")
        .call(d3.axisLeft(y).ticks(null, "s"))
        .append("text")
        .attr("x", 2)
        .attr("y", y(y.ticks().pop()) + 0.5)
        .attr("dy", "0.32em")
        .attr("fill", "#000")
        .attr("font-weight", "bold")
        .attr("text-anchor", "start")
        .text("Seats before snap election");

      var legend = g.append("g")
        .attr("font-family", "sans-serif")
        .attr("font-size", 10)
        .attr("text-anchor", "end")
        .selectAll("g")
        .data(keys.slice().reverse())
        .enter().append("g")
        .attr("transform", function(d, i) {
          return "translate(0," + i * 20 + ")";
        });

      legend.append("rect")
        .attr("x", width - 19)
        .attr("width", 19)
        .attr("height", 19)
        .attr("fill", z);

      legend.append("text")
        .attr("x", width - 24)
        .attr("y", 9.5)
        .attr("dy", "0.32em")
        .text(function(d) {
          return d;
        });

    });

    // ** Update data section (Called from the onclick)
    function updateData() {

      //call data
      d3.csv("data_copy.csv", function(d, i, columns) {
        for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
        return d;
      }, function(error, data) {
        if (error) throw error;

        var keys = data.columns.slice(1);

        //scale range of data again
        x0.domain(data.map(function(d) {
          return d.Year;
        }));
        x1.domain(keys).rangeRound([0, x0.bandwidth()]);
        y.domain([0, d3.max(data, function(d) {
          return d3.max(keys, function(key) {
            return d[key];
          });
        })]).nice();

        var sel = svg.selectAll("g")
          .data(data);
        //remove
        sel.exit().remove("g");

        sel.enter().append("g")
          .attr("transform", function(d) {
            return "translate(" + x0(d.Year) + ",0)";
          })
          .selectAll("rect")
          .data(function(d) {
            return keys.map(function(key) {
              return {
                key: key,
                value: d[key]
              };
            });
          })


        //remove
        svg.selectAll("rect");
        sel.exit().remove("rect");

        sel.enter().append("rect")
          .attr("x", function(d) {
            return x1(d.key);
          })
          .attr("y", function(d) {
            return y(d.value);
          })
          .attr("width", x1.bandwidth())
          .attr("height", function(d) {
            return height - y(d.value);
          })
          .attr("fill", function(d) {
            return z(d.key);
          });

        g.append("g")
          .attr("class", "axis")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x0));

        g.append("g")
          .attr("class", "axis")
          .call(d3.axisLeft(y).ticks(null, "s"))
          .append("text")
          .attr("x", 2)
          .attr("y", y(y.ticks().pop()) + 0.5)
          .attr("dy", "0.32em")
          .attr("fill", "#000")
          .attr("font-weight", "bold")
          .attr("text-anchor", "start")
          .text("Seats after snap election");
      });

    }

You need to update your rect selection in your click handler like this: 您需要像这样在点击处理程序中更新rect选项:

        ...
        //remove
        sel = svg.selectAll("rect");
        sel.exit().remove("rect");

        sel.enter().append("rect")
        sel.attr("x", function(d) {
            return x1(d.key);
          })
          .attr("y", function(d) {
            return y(d.value);
          })
       ...

You can see it in action here: http://plnkr.co/edit/3R9lauiQQIB0IgrAk3X2?p=preview 您可以在此处查看其运行情况: http : //plnkr.co/edit/3R9lauiQQIB0IgrAk3X2?p=preview

Edit - I've updated the plunker with a working example that addresses several other issues 编辑-我用一个解决其他几个问题的示例更新了插件

@thedude's answer is right, but doesn't correct everything: it updates the bars, but the heights are wrong, for instance. @thedude的答案是正确的,但并不能纠正所有问题:例如,它会更新横条,但是高度是错误的。 Something to do with the inner .data join and the subsequent secondary formatting, I guess. 我想这与内部.data连接和随后的辅助格式有关。

I checked at the same time and came up with the solution below. 我同时检查并提出了以下解决方案。 The core change that makes the update button update is this: 使更新按钮更新的核心更改是:

    var sel = svg.selectAll("g.chartarea").selectAll("g.year").data(data);
    sel.exit().remove();
    sel.enter().append("g").classed("year", true);
    // continuing with sel didn't update the just appended elements
    // so I repeated the selection to get the new elements as well
    sel = svg.selectAll("g.chartarea").selectAll("g.year");
    sel.attr( // and so on

Complete script: 完整的脚本:

    var svg = d3.select("svg"),
      margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 40
      },
      width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom,
      g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var x0 = d3.scaleBand()
      .rangeRound([0, width])
      .paddingInner(0.1);

    var x1 = d3.scaleBand()
      .padding(0.05);

    var y = d3.scaleLinear()
      .rangeRound([height, 0]);

    var z = d3.scaleOrdinal()
      .range(["#0087dc", "#d50000", "#FDBB30"]);

    // added class to enable precise selection
    g.append("g").classed("chartarea", true);

    // added classes to enable precise selection
    g.append("g")
      .classed("axis", true)
      .classed("x-axis", true);

    // added classes to enable precise selection
    g.append("g")
      .classed("axis", true)
      .classed("y-axis", true);

    updateGraph("data.csv");

    // ** Update data section (Called from the onclick)
    function updateData() {
      updateGraph("data_copy.csv");
    }

    function updateGraph(file) {

      //call data
      d3.csv(file, function(d, i, columns) {
        for (var i = 1, n = columns.length; i < n; ++i) d[columns[i]] = +d[columns[i]];
        return d;
      }, function(error, data) {
        if (error) throw error;

        var keys = data.columns.slice(1);

        //scale range of data again
        x0.domain(data.map(function(d) {
          return d.Year;
        }));
        x1.domain(keys).rangeRound([0, x0.bandwidth()]);
        y.domain([0, d3.max(data, function(d) {
          return d3.max(keys, function(key) {
            return d[key];
          });
        })]).nice();

        var sel = svg.selectAll("g.chartarea").selectAll("g.year")
          .data(data);
        //remove
        sel.exit().remove();

        // added classes to enable precise selection
        sel.enter().append("g").classed("year", true);
        sel = svg.selectAll("g.chartarea").selectAll("g.year");
        sel.attr("transform", function(d) {
            return "translate(" + x0(d.Year) + ",0)";
          })
          .attr("x", function(d) {
            return x1(d.key);
          })
          .attr("y", function(d) {
            return y(d.value);
          })
          .attr("width", x1.bandwidth())
          .attr("height", function(d) {
            return height - y(d.value);
          })
          .attr("fill", function(d) {
            return z(d.key);
          });

        var parties =
          sel.selectAll("rect.party")
          .data(function(d) {
            return keys.map(function(key) {
              return {
                key: key,
                value: d[key]
              };
            });
          });

        parties.exit().remove();
        // added classes to enable precise selection
        parties.enter().append("rect").classed("party", true);
        parties = sel.selectAll("rect.party");
        parties.attr("x", function(d) {
            return x1(d.key);
          })
          .attr("y", function(d) {
            return y(d.value);
          })
          .attr("width", x1.bandwidth())
          .attr("height", function(d) {
            return height - y(d.value);
          })
          .attr("fill", function(d) {
            return z(d.key);
          });

        // select the axes instead of appending them here
        g.selectAll("g.x-axis")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x0));

        g.selectAll("g.y-axis")
          .call(d3.axisLeft(y).ticks(null, "s"))
          .append("text")
          .attr("x", 2)
          .attr("y", y(y.ticks().pop()) + 0.5)
          .attr("dy", "0.32em")
          .attr("fill", "#000")
          .attr("font-weight", "bold")
          .attr("text-anchor", "start")
          .text("Seats after snap election");
      });

    }

Added some further changes that may be worth a look: 添加了一些可能值得一看的进一步更改:

  • Don't differentiate between initialization and update. 不要区分初始化和更新。 This is exactly what D3 excels at: doing everything with the same code. 这正是D3擅长的:用相同的代码完成所有工作。 In my refactored version, the code is reduced to a single updateGraph function that does both. 在我的重构版本中,代码被简化为一个可以同时执行这两个操作的updateGraph函数。

  • Use classes or identifiers to differentiate your graphical elements. 使用类或标识符来区分您的图形元素。 There are several places where you select too much with selectAll("g") which will select nearly all elements in your chart. 在很多地方,您都可以通过selectAll("g")选择太多,这几乎会选择图表中的所有元素。

  • Don't add stuff multiple times. 不要多次添加内容。 For example, the axes should be added only once. 例如,轴应仅添加一次。 In the original code, they were added twice, overlaying each other. 在原始代码中,它们被添加了两次,彼此重叠。 Instead, add them once, leave them uninitialized, then later select them and set their attributes correctly. 而是,将它们添加一次,使其未初始化,然后再选择它们并正确设置其属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM