点击按钮切换数据集时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
相关产品推荐
相关产品推荐

