将带连字符层级文本转为D3.js的flare数据,生成树状图不符预期
修复D3.js树状图层级解析问题
问题概述
输入层级文本后,生成的树状图层级混乱,二级节点b_2、叶子节点d_1/d_2的归属错误,不符合预期的层级结构。
输入文本数据
var inputText = ` - a - - b_1 - - - c_1 - - - c_2 - b - - b_2 - - - d_1 - - - d_2 `;
问题原因
原解析函数的层级判断逻辑存在两处核心错误:
- 使用
lastIndexOf('-') / 2 > depth计算层级,该公式完全不符合输入文本的层级规则,导致子节点归属判断错误。 - 循环处理节点时错误地执行
index +=1,可能跳过同级节点行,破坏层级结构。
解决方案
1. 修正文本解析函数
替换原parseHierarchicalText函数,通过统计每行的-数量确定层级,使用栈结构维护当前节点路径,确保子节点正确归属到父节点:
function parseHierarchicalText(text) { var lines = text.split('\n').filter(line => line.trim() !== ''); // 过滤空行 // 预处理每行,提取深度和节点名称 var lineData = lines.map(line => { var trimmed = line.trim(); var dashCount = (trimmed.match(/-/g) || []).length; var depth = dashCount - 1; // 1个-对应层级0,2个对应层级1,以此类推 var name = trimmed.replace(/-/g, '').trim(); return { depth, name }; }); var root = { name: 'Root', children: [] }; var stack = [{ node: root, depth: -1 }]; // 栈保存当前路径的节点及其层级 lineData.forEach(current => { // 找到当前节点的父节点:弹出栈中层级不小于当前层级的节点 while (stack[stack.length - 1].depth >= current.depth) { stack.pop(); } var parentNode = stack[stack.length - 1].node; var newNode = { name: current.name, children: [] }; parentNode.children.push(newNode); stack.push({ node: newNode, depth: current.depth }); }); return root; }
2. 优化绘图样式(可选)
添加连接线样式,确保线条可见:
.link { fill: none; stroke: #ccc; stroke-width: 2px; }
完整修正后的代码
function parseHierarchicalText(text) { var lines = text.split('\n').filter(line => line.trim() !== ''); var lineData = lines.map(line => { var trimmed = line.trim(); var dashCount = (trimmed.match(/-/g) || []).length; var depth = dashCount - 1; var name = trimmed.replace(/-/g, '').trim(); return { depth, name }; }); var root = { name: 'Root', children: [] }; var stack = [{ node: root, depth: -1 }]; lineData.forEach(current => { while (stack[stack.length - 1].depth >= current.depth) { stack.pop(); } var parentNode = stack[stack.length - 1].node; var newNode = { name: current.name, children: [] }; parentNode.children.push(newNode); stack.push({ node: newNode, depth: current.depth }); }); return root; } var inputText = ` - a - - b_1 - - - c_1 - - - c_2 - b - - b_2 - - - d_1 - - - d_2 `; var data = parseHierarchicalText(inputText); // 设置图表尺寸和边距 var margin = { top: 20, right: 90, bottom: 30, left: 90 }, width = 800 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; // 添加SVG容器 var svg = d3.select("#dendrogram") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // 创建层级布局 var hierarchy = d3.hierarchy(data, d => d.children); var cluster = d3.cluster().size([height, width - 100]); var root = cluster(hierarchy); // 绘制连接线 svg.selectAll('path') .data(root.links()) .enter() .append('path') .attr('class', 'link') .attr('d', d3.linkHorizontal() .x(d => d.y) .y(d => d.x) ); // 绘制节点 var nodes = svg.selectAll('g.node') .data(root.descendants()) .enter() .append('g') .attr('class', d => 'node' + (d.children ? ' node--internal' : ' node--leaf')) .attr('transform', d => 'translate(' + d.y + ',' + d.x + ')'); nodes.append('circle').attr('r', 7); nodes.append('text') .attr('dy', '.35em') .attr('x', d => d.children ? -13 : 13) .style('text-anchor', d => d.children ? 'end' : 'start') .text(d => d.data.name);
效果说明
修正后,树状图将正确呈现预期层级:
- Root节点下包含
a、b两个一级节点 a节点的子节点为b_1,b_1的子节点为c_1、c_2b节点的子节点为b_2,b_2的子节点为d_1、d_2
内容的提问来源于stack exchange,提问作者Cybernetic
相关产品推荐
相关产品推荐

