D3 在数据更新时删除以前的条形图

D3 removing previous bar chart on update of data

当数据更新时,我无法从条形图中删除条形。我希望 .exit().remove() 工作。但是当我更新时,两个条形图仍然存在。

gh-pages 的最低版本是 运行,您需要使用下拉菜单来启动它。目前第一个数据文件 data1.json 没有加载。

加载文件时,仅显示与上次呈现中的名称关联的栏。当再次单击下拉菜单中的同一文件时,会出现其他栏。所以文件必须被点击两次才能正确显示。

如果初始呈现仅显示公共条,那么这就解释了为什么在页面首次加载时它是空白的,没有任何共同点。

无论如何,显然这不是我想要的行为。

代码在 github and on jsFiddle

          var newRects0 = g0.selectAll(".bar")
            .data(data, function(d) {
              return d.name;
            });

          // newRects0.selectAll(".bar").remove();
          newRects0.enter()
            .append('rect')
            .attr('x', function(d, i) {
              return x(d.name);
            })
            // .attr('opacity', 0.33)
            .attr('width', x.bandwidth())
            .attr('y', height)
            .attr('height', 0)
            .attr("transform", "translate(0,0)")
            .style('fill', 'gray')
            .attr("class", function(d, i) {
              return "group" + i + " bar"
            });

          newRects0.exit().remove();

          newRects0.transition()
            .duration(100)
            .delay(function(d, i) {
              return i * 15
            })
            .attr("x", function(d) {
              return x(d.name);
            })
            .attr("y", function(d, i) {
              return y(d.clocktime);
            })
            .attr("height", function(d, i) {
              return height - y(d.clocktime)
            })
            .attr("width", x.bandwidth());

x 轴和 y 轴变化正确。

更新:

我现在正在尝试使用 Bostock 的 Update Pattern III 条形出现然后在我认为 750 毫秒后消失。

我已经大大简化了代码,现在在githubgh-pagesjsFiddle .

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
  .container {
    margin-left: 0px;
  }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" crossorigin="anonymous"></script>

<body>
  <ul class="nav nav-tabs">
    <li class="nav-item">
      <a class="nav-link active" href="#">Home</a>
    </li>
    <li class="nav-item dropdown">
      <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Data</a>
      <div class="dropdown-menu">
        <a class="dropdown-item" id="defaultInput" value="one" href="#">data1.json</a>
        <a class="dropdown-item" id="updateInput" value="two" href="#">data2.json</a>
      </div>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">Link</a>
    </li>
    <li class="nav-item">
      <a class="nav-link disabled" href="#">Disabled</a>
    </li>
  </ul>
  <div class="container-fluid">
    <div class="row">
      <div class="col-md-12">
        <div id="race_graph">
        </div>
      </div>
    </div>
  </div>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    // set the dimensions and margins of the graph
    var margin = {
        top: 70,
        right: 50,
        bottom: 100,
        left: 80
      },
      width = 1400 - margin.left - margin.right,
      height = 600 - margin.top - margin.bottom;

    var parseTime = d3.timeParse("%M:%S");
    var timeformat = d3.timeFormat("%M:%S");

    // set the domains and ranges
    var x = d3.scaleBand()
      .range([0, width])
      .padding([0.6]);

    // temporal y-scale
    var y = d3.scaleTime()
      .range([height, 0]);

    var xAxis = d3.axisBottom(x);

    var yAxis = d3.axisLeft(y)
      .ticks(7)
      .tickFormat(d3.timeFormat("%M:%S"));

    // Add main graph svg
    var svg = d3.select("#race_graph")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom);

    // Add groups for main bar chart
    var g0 = svg.append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


    function defaultFunction() {
      d3.json("data1.json", function(error, newdata) {
        if (error) throw error;
        data = newdata;

        data.forEach(function(d) {
          d.racetime = parseTime(d.racetime);
          d.handicap = parseTime(d.handicap);
          d.clocktime = parseTime(d.clocktime);
          d.place = +d.place;
          d.points = +d.points;
          d.raceplace = +d.raceplace;
          d.timeplace = +d.timeplace;
        })
        update();
      });
    }

    function updateFunction() {
      d3.json("data2.json", function(error, newdata) {
        if (error) throw error;
        data = newdata;
        data.forEach(function(d) {
          d.racetime = parseTime(d.racetime);
          d.handicap = parseTime(d.handicap);
          d.clocktime = parseTime(d.clocktime);
          d.place = +d.place;
          d.points = +d.points;
          d.raceplace = +d.raceplace;
          d.timeplace = +d.timeplace;
        })
        update(data);
      });
    }

    function update(err, newdata) {

      // set the domains
      x.domain(data.map(function(d) {
        return d.name
      }));

      // set domain temporal y-scale
      y.domain([parseTime('00:00'), d3.max(data, function(d) {
        return d.clocktime
      })]);

      // Add the X Axis
      svg.select(".x.axis").call(xAxis);
      svg.select(".x.axis").remove();
      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(" + (margin.left) + "," + (height + margin.top) + ")")
        .call(xAxis)
        .selectAll("text")
        .attr("transform", "translate(-100,0)")
        .style("text-anchor", "end")
        .style("font", "7px times")
        .attr("class", function(d, i) {
          return "groupText" + i + " xAxisText"
        })
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-75)");

      // Add the y axis on left
      svg.select(".y.axis").remove();
      svg.append("g")
        .attr("class", "y axis")
        .attr("transform", "translate(" + (margin.left) + "," + margin.top + ")")
        .call(yAxis);

      var t = d3.transition()
        .duration(4000);

      // JOIN new data with old elements
      var newRects0 = g0.selectAll(".bar")
        .data(data, function(d) {
          return d;
        });

      // EXIT old elements not present in new data
      newRects0.exit()
        .transition(t)
        .attr("x", function(d) {
          return x(d.name);
        })
        .attr("y", function(d, i) {
          return y(d.clocktime);
        })
        .attr("height", function(d, i) {
          return height - y(d.clocktime)
        })
        // .attr("width", x.bandwidth())
        .remove();

      // UPDATE old elements present in new data
      newRects0.attr("x", function(d) {
          return x(d.name);
        })
        .attr("y", function(d, i) {
          return y(d.clocktime);
        })
        .attr("height", function(d, i) {
          return height - y(d.clocktime)
        })
        // .attr("width", x.bandwidth())
        .style('fill', 'gray');

      // ENTER new elements present in new data
      newRects0.enter()
        .append('rect')
        .attr("x", function(d) {
          return x(d.name);
        })
        .attr("y", function(d, i) {
          return y(d.clocktime);
        })
        .attr("height", function(d, i) {
          return height - y(d.clocktime)
        })
        .attr("width", x.bandwidth())
        .style('fill', 'gray')
        .attr("class", function(d, i) {
          return "group" + i + " bar"
        });

    }; //closes update() function

    document.getElementById("defaultInput")
      .onclick = defaultFunction;
    document.getElementById("updateInput")
      .onclick = updateFunction;
    defaultFunction();
  </script>
</body>

谢谢,

我绘制函数的第一行是:

d3.selectAll("svg").remove();

这会在创建新的 svg(取决于 window 大小)并使用导入的新数据更新它之前清除 svg 和所有关联的对象。

您可以 select 单独删除要删除的矩形,但如果轴根据新数据发生变化,则可能值得删除所有内容并重新绘制轴。

编辑:我认为混淆在 "defaultFunction"/"updateFunction"。

 document.getElementById("defaultInput")
  .onclick = defaultFunction;
document.getElementById("updateInput")
  .onclick = updateFunction;
defaultFunction();

对于 "updateInput",data1.json 和 data2.json 分别由 updateFunction 和 defaultFunction 加载。

如果能分离出图表功能,那就有

function input1() {
d3.json("data1.json", function(error, data) {
    if (error) {
        return console.error(error);
    } else
 update(data);
})}

 function input2() {
d3.json("data2.json", function(error, data) {
    if (error) {
        return console.error(error);
    } else
 update(data);
})}

function update (data){
remove old data;
parse new data;
make graph...
}

这应该会清除它。

我终于弄明白了 Bostock 的示例 Update Pattern III 并将其应用于条形图。

这是 github and a demo on gh-pages 上的代码。

此模式的阶段是,

  • 加入旧元素的新数据
  • 退出新数据中不存在的旧元素
  • 更新新数据中存在的旧元素
  • 输入新数据中的新元素