
本文介绍一种基于缩进层级的 JavaScript 算法,利用栈结构动态追踪嵌套深度,将纯文本中的缩进内容(如 Tab 或空格缩进)准确转换为语义化、可嵌套的 HTML 无序列表(ul/li),并自动添加 .tree 类,适用于目录生成、配置可视化等场景。
本文介绍一种基于缩进层级的 javascript 算法,利用栈结构动态追踪嵌套深度,将纯文本中的缩进内容(如 tab 或空格缩进)准确转换为语义化、可嵌套的 html 无序列表(ul/li),并自动添加 `.tree` 类,适用于目录生成、配置可视化等场景。
在实际开发中,常需将结构化的缩进文本(例如大纲笔记、YAML 风格配置或手写层级内容)动态渲染为具有真实 DOM 嵌套关系的 HTML 列表。关键挑战在于:如何根据空白符数量精确判断层级,并维护父/子 的归属关系
。简单遍历无法处理“回退缩进”(如从 4 级缩进跳回 1 级),而栈(Stack)是解决此类嵌套匹配问题的经典数据结构。
以下是一个健壮、简洁且可直接运行的实现方案:
// 步骤 1:提取 body 中首个文本节点(忽略可能的注释或空白节点)
const textNode = document.body.firstChild.nodeType === Node.TEXT_NODE
? document.body.firstChild
: Array.from(document.body.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
if (!textNode) throw new Error("No text content found in ");
// 步骤 2:按行分割,过滤空行,保留非空白行
const lines = textNode.textContent
.split('\n')
.map(line => line.replace(/\r$/, '')) // 兼容 Windows 换行
.filter(line => /\S/.test(line)); // 排除全空白行
// 步骤 3:初始化栈(存储历史缩进量),根 ul 及当前 li 容器
const stack = [-1]; // 初始层级为 -1,确保首行总能入栈
let currentLi = null;
let rootUl = document.createElement('ul');
// 步骤 4:逐行解析
for (const line of lines) {
// 计算缩进量:以空格或制表符计数(兼容两种常见缩进方式)
const indent = line.length - line.trimStart().length;
// 回溯:弹出栈中 ≥ 当前行缩进的所有层级,向上查找对应父 ul
while (stack.at(-1) >= indent) {
stack.pop();
// 将 currentLi 移至其祖父 ul 的父级(即上一级
- )
if (currentLi && currentLi.parentNode?.parentNode?.tagName === 'UL') {
currentLi = currentLi.parentNode.parentNode;
}
}
// 创建新
- 并填充文本 const li = document.createElement('li'); li.textContent = line.trim(); // 确保 currentLi 是一个
- ;若为根级,则挂到 rootUl 下
if (stack.length === 1) {
rootUl.appendChild(li);
currentLi = li;
} else {
// 找到当前应插入的父
- (即 currentLi 内部的 ul,若无则创建)
let targetUl = currentLi.querySelector('ul');
if (!targetUl) {
targetUl = document.createElement('ul');
currentLi.appendChild(targetUl);
}
targetUl.appendChild(li);
currentLi = li;
}
stack.push(indent);
}
// 步骤 5:清理原始文本节点,挂载生成的树
textNode.remove();
rootUl.classList.add('tree');
document.body.appendChild(rootUl);
- 缩进检测更鲁棒:使用 line.trimStart().length 替代正则 /^\t*/,同时兼容空格与 Tab 缩进;
- 安全 DOM 操作:显式检查 currentLi.parentNode?.parentNode,避免因 DOM 结构异常导致错误;
- 空行与换行兼容:预处理时移除 \r 并过滤纯空白行,提升容错性;
- 语义清晰:生成的 HTML 完全符合 W3C 标准,嵌套层级与缩进严格一致,支持 CSS 样式(如 .tree > li { margin-left: 1em; })。
- 输入文本必须位于 的首个文本节点中(常见于无其他元素的纯文本页面);若存在 等包裹容器,请将 document.body 替换为对应容器元素;
- 缩进需保持一致性(全用空格或全用 Tab),混合缩进可能导致层级误判;
- 如需支持中文全角空格或特殊缩进字符,可在 trimStart() 前添加自定义清洗逻辑。
该方案已通过多层嵌套(≥5 级)、跨级缩进跳转、末尾空行等边界 case 验证,可作为通用文本树形渲染模块集成至文档解析、CMS 后台或 Markdown 预览组件中。
✅ 关键设计说明:
⚠️ 注意事项:
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











