使用D3删除预定义DOM元素报错 exit()返回异常如何解决
使用D3删除DOM元素失败
问题复现
操作直接在HTML中预定义的DOM元素,初始HTML结构:
<div> <p class="child" id="1">James</p> <p class="child" id="2">Kate</p> </div>
初始编写的D3代码:
var div = d3.select('div') var pp = div.selectAll('p.child'); var ppUpdate = pp.data(dataset, d =>d.id); var ppexit = ppUpdate.exit(); ppexit.remove();
测试使用的数据集:
var data1 = [{ id: 1, name: 'James' }, { id: 4, name: 'Jordan' }];
代码运行到pp.data()语句时触发报错。添加非空判断修改key函数后报错消失:
var ppUpdate = pp.data(dataset, d => { if(d){ return d.id } });
但此时ppUpdate.exit()返回了HTML中全部2条已存在的p元素,没有按预期返回需要删除的id为2的元素,调试发现D3内部bindKey函数无法获取正确的keyValue。
故障原因
D3的data()方法传入的key匹配函数,在遍历页面上已存在、未绑定过D3数据的DOM元素时,入参d是undefined(这类预写在HTML里的DOM从未绑定过D3管理的数据):
- 直接访问
d.id会触发类型错误,这是初始代码报错的根因 - 添加非空判断后,所有现有DOM返回的key值都是
undefined,和传入数据集里的id(1、4)完全无法匹配,D3会判定所有现有DOM都属于需要移除的exit部分,因此返回全部两个p元素。
解决方案
方案1:兼容现有DOM的匹配逻辑
key函数同时适配两种场景:遍历初始无数据的DOM时,从DOM本身的id属性取值做匹配;遍历已绑定数据的元素时,从数据对象取id。注意不要用箭头函数,否则无法获取当前DOM的this上下文:
const dataset = [{ id: 1, name: 'James' }, { id: 4, name: 'Jordan' }]; const div = d3.select('div') const pp = div.selectAll('p.child'); const ppUpdate = pp.data(dataset, function(d) { return d ? d.id : this.id; }); ppUpdate.exit().remove(); // 仅会移除id=2的p元素,符合预期
方案2:全量重渲染
如果不需要保留初始DOM的自定义状态,可以直接清空容器后走标准的D3 enter/update/exit渲染流程,不需要处理初始DOM的匹配问题:
const dataset = [{ id: 1, name: 'James' }, { id: 4, name: 'Jordan' }]; const div = d3.select('div').html(''); const pp = div.selectAll('p.child') .data(dataset, d => d.id); pp.exit().remove(); const ppEnter = pp.enter() .append('p') .attr('class', 'child') .attr('id', d => d.id) .text(d => d.name) // 已有元素的内容更新逻辑可通过pp.merge(ppEnter) 统一处理
内容的提问来源于stack exchange,提问作者moneybags
相关产品推荐
相关产品推荐

