D3.js开发福布斯百强企业交互式条形图报data未定义错误
报错原因与代码问题排查
核心报错data not defined根因
d3.csv()是异步请求,传入.then()的回调函数里的data是回调作用域的局部变量,定义在全局的update()函数访问不到这个局部变量,所以执行到d3.max(data, d => d[xValue])时会找不到data对象。
修复方式:在d3.csv调用之前的全局作用域先声明存储数据的变量:
let allData = null
然后在d3.csv的回调里,完成数据类型转换后把处理好的数据赋值给这个全局变量,再调用update:
d3.csv("data/data_clean.csv").then(data => { data.forEach(d => { d.sales_usd_billion = Number(d.sales_usd_billion) d.profit_usd_billion = Number(d.profit_usd_billion) d.rank = Number(d.rank) // 原代码漏了rank的类型转换,排序会出现异常 }) allData = data update() })
后续update函数里所有用到data的位置,全部替换成allData即可解决这个报错。
其他现存代码问题
- 坐标轴组变量名写错:提前定义的坐标轴组是
xAxisGroup、yAxisGroup,但update函数里写的是xAxis.transition()、yAxis.transition(),变量名不匹配会触发新的未定义报错,需要把对应位置的变量名改对。 - 排序逻辑位置错误:当前代码先给x、y比例尺设置domain,再对数据排序,排序后的顺序不会同步到比例尺定义域,条形顺序不会随排序更新,需要把排序逻辑移到设置x、y.domain()之前执行。
- 条形图渲染逻辑存在多处错误:
- 没有定义
rects选择集,直接调用rects.enter()会报错,需要先通过selectAll绑定数据生成选择集 - 条形宽度取值用了未定义的
value变量,应该替换成前面获取的当前指标变量xValue - 条形高度写死为4,会导致条形挤在一起,应该用y比例尺的
y.bandwidth()获取每个条形的预留高度,可适当减去边距做留白 - 没有处理更新、退出的选择集,切换指标时旧条形不会更新,只会不断追加新矩形,需要补上merge、remove逻辑
- 没有定义
- y轴band比例尺没有设置padding,条形之间没有间隙,可读性差
- x轴标签没有动态更新逻辑,切换销售额、利润指标时,x轴标题不会同步变化
- 漏了
rank字段的类型转换,csv读入的rank默认是字符串类型,直接做减法排序会出现结果异常,需要在数据预处理阶段把rank转成数字类型。
修复后的核心update函数参考
function update() { const t = d3.transition().duration(750) // 获取当前选中指标 const xValue = $("#var-select").val() // 先按当前指标排序 allData.sort((a,b) => b[xValue] - a[xValue]) // 再设置比例尺domain x.domain([0, d3.max(allData, d => d[xValue])]) y.domain(allData.map(d => d.company)).padding(0.2) // 加20%的条间距 // 更新坐标轴 xAxisCall.scale(x) xAxisGroup.transition(t).call(xAxisCall) yAxisCall.scale(y) yAxisGroup.transition(t).call(yAxisCall) // 动态更新x轴标签 d3.select(".x.axis-label").text(xValue === "sales_usd_billion" ? "销售额(十亿美元)" : "利润(十亿美元)") // 渲染条形,处理enter/update/exit全流程 const rects = g.selectAll("rect.bar") .data(allData, d => d.company) // 用公司名做匹配key,保证动画过渡正确 // 删除退出的旧元素 rects.exit().remove() // 新增元素+合并更新集,做过渡动画 rects.enter().append("rect") .attr("class", "bar") .attr("y", d => y(d.company)) .attr("x", 0) .attr("fill", "steelblue") .merge(rects) .transition(t) .attr("y", d => y(d.company)) .attr("width", d => x(d[xValue])) .attr("height", y.bandwidth()) }
内容的提问来源于stack exchange,提问作者pomenelo
相关产品推荐
相关产品推荐

