本文介绍一种基于缩进层级的 JavaScript 算法,利用栈结构动态追踪嵌套深度,将纯文本中的缩进内容(如制表符或空格)精准转换为符合语义的嵌套 树形列表,并添加 .tree 类以支持样式控制。
本文介绍一种基于缩进层级的 javascript 算法,利用栈结构动态追踪嵌套深度,将纯文本中的缩进内容(如制表符或空格)精准转换为符合语义的嵌套 `
- ` 树形列表,并添加 `.tree` 类以支持样式控制。
在构建动态树形菜单、文档大纲解析或从纯文本生成结构化 HTML 的场景中,常需将具有视觉缩进关系的文本(如 Markdown 风格的层级列表)转化为标准嵌套 DOM 结构。关键挑战在于:准确识别每一行的缩进级别,并据此决定其应插入到哪一层
- 中
上述问题中提供的原始 HTML 仅包含扁平的文本节点(无标签),且缩进由空格(非 Tab)构成。因此,正确方案需满足三点:
✅ 正确计算每行首部空白字符数(line.length - line.trim().length);
✅ 使用栈(stack)记录已打开的各级缩进深度,实现“回溯闭合”逻辑;
✅ 动态查找或创建当前层级对应的- ,避免手动维护 currentUl/currentLi 状态导致的错位。
以下是经过验证、简洁健壮的核心实现:
// 1. 提取所有非空行(跳过纯空白行) const lines = Array.from( document.body.firstChild.textContent.matchAll(/^[^\S\r\n]*\S.*$/gm), match => match[0] ); // 2. 初始化栈(初始层级为 -1,确保首行总能被追加) const stack = [-1]; let currentLi = document.body; // 临时父容器,后续将被移除 let currentUl; for (const line of lines) { const indent = line.length - line.trim().length; // 3. 回溯:弹出栈顶所有 ≥ 当前行缩进的层级,向上找到父-
while (stack.at(-1) >= indent) {
stack.pop();
currentLi = currentLi.parentNode.parentNode; // 跳过
- →
- → 上级
- } // 4. 确保当前
- 下存在
- (用于容纳子项),不存在则创建
currentUl = currentLi.querySelector('ul') || document.createElement('ul');
currentLi.appendChild(currentUl);
// 5. 创建新
- 并插入文本内容
currentLi = document.createElement('li');
currentLi.textContent = line.trim();
currentUl.appendChild(currentLi);
// 6. 记录当前缩进层级
stack.push(indent);
}
// 7. 清理原始文本节点,并为根
- 添加 class
document.body.firstChild.remove();
document.body.querySelector('ul').classList.add('tree');
- 本方案假设缩进由空格构成(与示例一致);若含 Tab 字符,建议先用 line.replace(/\t/g, ' ') 统一为 4 空格再计算 indent;
- matchAll(/^[^\S\r\n]*\S.*$/gm) 精确匹配非空行(排除纯空白行),比 split('\n') 更鲁棒;
- currentLi.parentNode.parentNode 的跳转逻辑依赖于 DOM 结构:
- →
- →
- (上级),确保层级关系不乱;
- 最终生成的
- 可配合 CSS 实现折叠/展开、颜色标识等交互效果,例如:
.tree { margin: 0; padding-left: 0; } .tree li { list-style: none; margin: 4px 0; } .tree ul { margin: 0 0 0 20px; padding-left: 0; }
该方法时间复杂度为 O(n),空间复杂度为 O(d)(d 为最大嵌套深度),兼具可读性与工程实用性,适用于各类基于缩进的结构化文本解析任务。
⚠️ 注意事项:
- 并插入文本内容
currentLi = document.createElement('li');
currentLi.textContent = line.trim();
currentUl.appendChild(currentLi);
// 6. 记录当前缩进层级
stack.push(indent);
}
// 7. 清理原始文本节点,并为根
- →
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











