You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击按钮切换数据集时D3图表无法更新如何解决?

问题原因

  • 只处理了D3数据绑定的enter阶段,未处理已存在元素的更新和销毁:首次渲染时#chart容器内无g元素,enter逻辑会创建对应元素完成渲染;二次调用时容器内已有对应数量的g元素,enter逻辑不会触发,也没有逻辑更新旧元素的属性、销毁多余元素,因此图表无变化。
  • 存在D3版本冲突:HTML中同时引入了D3 v4和D3 v3.5.17两个版本,两个版本API不兼容,可能引发额外的渲染异常。
  • 冗余重复创建:原代码每次调用drawChart都会在body新增一个tooltip类的div,多次调用会生成大量冗余DOM元素。

修复方案

方案1(简单快速):每次渲染前清空容器内旧元素

在drawChart函数开头加入清空逻辑即可,修改后的drawChart代码:

// 可把tooltip创建逻辑挪到外面,只创建一次
const div = d3.select("body").append("div").attr("class", "tooltip");

function drawChart(dataSet, prop) {
  // 新增:清空之前渲染的所有内容
  d3.select("#chart").selectAll("*").remove();
  let width = 900;
  let height = 200;
  let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.9);
  let y = d3.scale
    .linear()
    .domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
    .range([height, 0]);
  let chart = d3.select("#chart").attr("width", width).attr("height", height);
  let barWidth = width / dataSet.length;
  let bar = chart
    .selectAll("g")
    .data(dataSet)
    .enter()
    .append("g")
    .attr("transform", function (d, i) {
      return "translate(" + i * barWidth + ",0)";
    });
  bar
    .append("rect")
    .attr("y", function (d) {
      return y(d[prop]);
    })
    .attr("height", function (d) {
      return height - y(d[prop]);
    })
    .attr("width", barWidth);
}

方案2(标准D3写法):完整实现enter/update/exit生命周期

适合后续需要加过渡动画的场景,性能也更优:

const div = d3.select("body").append("div").attr("class", "tooltip");

function drawChart(dataSet, prop) {
  let width = 900;
  let height = 200;
  let x = d3.scale.ordinal().rangeRoundBands([0, width], 0.9);
  let y = d3.scale
    .linear()
    .domain([dataSet[0][prop] - 39, dataSet[dataSet.length - 1][prop]])
    .range([height, 0]);
  let chart = d3.select("#chart").attr("width", width).attr("height", height);
  let barWidth = width / dataSet.length;

  // 绑定数据
  let barGroups = chart.selectAll("g").data(dataSet);

  // exit阶段:移除多余的旧元素
  barGroups.exit().remove();

  // enter阶段:新增不足的元素
  const newBarGroups = barGroups.enter()
    .append("g");
  
  // 给新增的g添加rect子元素
  newBarGroups.append("rect");

  // update阶段:更新所有新旧元素的属性(合并enter和update的选择集)
  const allBarGroups = newBarGroups.merge(barGroups);
  allBarGroups.attr("transform", function (d, i) {
      return "translate(" + i * barWidth + ",0)";
    })
    .select("rect")
    .attr("y", function (d) {
      return y(d[prop]);
    })
    .attr("height", function (d) {
      return height - y(d[prop]);
    })
    .attr("width", barWidth);
}

额外修复:删除重复引入的D3

修改HTML部分,删掉head里的D3 v4引入,只保留body里的v3版本即可,修改后的HTML:

<!DOCTYPE html>
<html>
  <meta charset="utf-8" />
  <head>
  </head>
  <body>
    <div id="app">
      <svg class="chart" id="chart"></svg>
      <button id="dataA">data1</button>
      <button id="dataB">data2</button>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
    <script src="./index.js"></script>
  </body>
</html>

内容的提问来源于stack exchange,提问作者Joa0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 20:15:07