本文详解如何使用递归 JavaScript 函数将 HTML 中任意深度的嵌套 <ul> 结构(含 <li class="operator"> 和 <li class="condition">)精准解析为等价的嵌套数组,保留层级关系与文本语义。
本文详解如何使用递归 javascript 函数将 html 中任意深度的嵌套 `
- ` 结构(含 `
- ` 和 `
- `)精准解析为等价的嵌套数组,保留层级关系与文本语义。
在构建动态规则引擎、可视化逻辑编辑器或 DSL 解析器时,常需将用户通过拖拽/缩进生成的嵌套列表(如“操作符 → 条件”树形结构)转化为可序列化、可计算的 JavaScript 数组。核心挑战在于:<ul> 不是 <li> 的直接子元素,而是紧跟在某个 <li> 后的兄弟节点——但语义上它属于该 <li> 的子级。因此,不能简单遍历所有 <li>,而应按 DOM 层级顺序逐层处理,并将紧随其后的 <ul> 视为其父 <li> 的嵌套子结构。
以下是一个健壮、可复用的递归解析函数:
const parseListItems = (element) => { const resultArray = []; // 遍历当前 ul 的直接子节点(非仅 li,因 ul 是 li 的兄弟) Array.from(element.children).forEach(node => { if (node.tagName === 'LI') { const textContent = node.textContent.trim(); const nestedUl = node.querySelector('ul'); // 查找该 li 内部或紧邻的 ul(标准 HTML 中 ul 应在 li 内) if (nestedUl && nestedUl.parentElement === node) { // ul 是当前 li 的直接子元素:递归解析并拼入数组 const nestedResult = parseListItems(nestedUl); resultArray.push([textContent, ...nestedResult]); } else { // 无嵌套 ul:直接推入纯文本(保留原始语义,如 "operator1" 或 "condition1") resultArray.push(textContent); } } }); return resultArray; };✅ 关键设计说明:
- 使用 element.children(而非 querySelectorAll('li'))确保按 DOM 顺序处理,避免遗漏或错位;
- 显式检查 nestedUl.parentElement === node,防止误捕父级或无关 <ul>;
- 对每个 <li> 提取 textContent.trim(),自动剥离换行与空格,兼容多行 HTML 缩进;
- 采用 [textContent, ...nestedResult] 语法,天然支持多子项(如 operator2 下两个 condition),生成形如 ["operator2", ["condition1"], ["condition2"]] 的结构。
? 使用示例(配合 DOM 加载):
document.addEventListener('DOMContentLoaded', () => { const rootUl = document.querySelector('ul'); const tree = parseListItems(rootUl); console.log(tree); // 输出:["operator1", ["operator2", ["condition1"], ["condition2"]], ["condition3"]] });⚠️ 注意事项:
- 输入 HTML 必须符合规范:嵌套 <ul> 应作为 <li> 的子元素(而非同级兄弟),否则需预处理修正 DOM 结构;
- 若 <li> 内含其他标签(如 <span>、<strong>),textContent 仍能准确提取全部文本;如需保留 HTML 结构,请改用 innerText 或定制文本提取逻辑;
- 该函数不依赖 class 名称(如 "operator" / "condition"),仅基于 DOM 层级和标签结构,语义由业务层后续解析。
此方案简洁、无外部依赖、深度适配真实 HTML 树,可直接集成至 React/Vue 组件或 Node.js JSDOM 环境中进行服务端解析。

















