Highcharts组织架构图删除选中节点失效 始终删除最末端节点
组织架构图删除节点始终删除末端节点的问题修复
根因定位
Highcharts 类型为organization的组织架构图series,渲染逻辑和常规图表存在核心差异:
- 写入
series.data的['from', 'to']数组本质是节点间的连线(边)数据,不是独立的节点实体 - 点击节点触发的
select事件中,回调上下文this指向的是和节点绑定的边point实例,而非独立节点对象 - 原代码直接将这个边实例存在全局变量
selectedNode中,由于边对象引用会随图表重绘被动态覆盖,最终残留的始终是最后渲染的最底层节点对应的边引用,因此每次触发删除时,操作的都是这个被覆盖的末端节点引用。
修复实现
- 在series配置中新增独立
nodes配置项,给每个节点分配唯一id、展示元信息,让Highcharts可以独立追踪每个节点实体,不再仅靠连线关系推导节点 - 调整选中事件的存储逻辑,仅记录选中节点的唯一id,不直接存储生命周期不稳定的point对象引用
- 删除节点时,同步过滤掉所有和目标节点关联的连线、移除nodes数组中对应的节点项,再主动传入更新后的数据触发图表刷新,避免使用原有缓存的引用。
修正后的核心代码
// 全局仅存储选中节点的唯一id,避免不稳定的对象引用问题 let selectedNodeId = undefined; let chart = Highcharts.chart('container', { chart: { height: "40%", inverted: true }, credits: { enabled: false }, title: { text: 'EA Organogram' }, series: [{ type: 'organization', name: 'Employee Name', keys: ['from', 'to'], data: [["manager", "employee1"], ["manager", "employee2"], ["manager", "employee3"], ["manager", "employee4"], ["employee2", "employee5"], ["employee2", "employee6"] ], // 新增独立nodes配置,逐一定义每个节点的元信息 nodes: [ { id: 'manager', name: '部门负责人' }, { id: 'employee1', name: '员工1' }, { id: 'employee2', name: '员工2' }, { id: 'employee3', name: '员工3' }, { id: 'employee4', name: '员工4' }, { id: 'employee5', name: '员工5' }, { id: 'employee6', name: '员工6' } ], colorByPoint: false, color: '#bed308', dataLabels: { color: 'black' }, borderColor: 'black', nodeWidth: 65, }], tooltip: { outside: true }, exporting: { allowHTML: true, sourceWidth: 800, sourceHeight:600 }, plotOptions:{ series:{ allowPointSelect: true, point:{ cursor: 'pointer', events:{ select: function (event) { // 提取当前点击关联的节点id const currentNodeId = this.options.from === selectedNodeId ? this.options.to : this.options.from; // 清除上一个选中节点的高亮状态 if (selectedNodeId !== undefined && selectedNodeId !== currentNodeId) { const prevNode = chart.series[0].nodes.find(item => item.id === selectedNodeId); if (prevNode) prevNode.setState(''); } // 记录当前选中的节点id selectedNodeId = currentNodeId; // 给当前选中节点设置高亮态 const targetNode = chart.series[0].nodes.find(item => item.id === selectedNodeId); if (targetNode) targetNode.setState('select'); }, unselect: function () { // 清除选中态 if (selectedNodeId !== undefined) { const targetNode = chart.series[0].nodes.find(item => item.id === selectedNodeId); if (targetNode) targetNode.setState(''); } selectedNodeId = undefined; } } } } } }); $('#delNode').on('click', function(){ if(selectedNodeId === undefined){ alert("Select a node!"); return; } const activeSeries = chart.series[0]; // 过滤所有和选中节点关联的连线 const validData = activeSeries.data .filter(point => point.options.from !== selectedNodeId && point.options.to !== selectedNodeId) .map(point => [point.options.from, point.options.to]); // 过滤掉nodes数组里的选中节点 const validNodes = activeSeries.options.nodes.filter(node => node.id !== selectedNodeId); // 传入更新后的数据刷新图表 activeSeries.update({ data: validData, nodes: validNodes }); // 清空选中状态 selectedNodeId = undefined; });
注意:不要直接在删除操作后无参调用
series.update(),该方法会复用原有缓存的边对象引用,依旧会触发删错节点的异常,必须显式传入过滤后的连线数据和节点配置。
内容的提问来源于stack exchange,提问作者azim
相关产品推荐
相关产品推荐

