本文详解为何原递归函数无法处理 `
` 等非 `li` 元素,并提供基于 `for...of` 的健壮解决方案,确保所有非 `li` 元素(如 `
`、``、`
问题根源在于 NodeList.prototype.forEach() 的执行时序与 DOM 树动态变更的冲突。当递归调用 testFn(e) 后立即执行 e.replaceWith(...e.childNodes) 时,e 被从 DOM 中移除,其子节点被插入到原位置——这会修改当前 node.childNodes 的长度和索引顺序。而 forEach() 内部是基于初始快照遍历的,后续迭代仍按原始 NodeList 进行,导致部分节点被跳过(尤其是紧邻被替换节点之后的兄弟节点),造成
等元素未被处理。
使用 for...of 循环可规避该问题,因为它在每次迭代时都重新获取当前 childNodes 的迭代器,能响应实时 DOM 变化;更重要的是,它使控制流更清晰、避免闭包陷阱,便于逻辑调试。
以下是修正后的完整实现:
let html = `
- foo link text;
- bar link text;
Paragraph text baz and biz text.
Paragraph text.
`; html = `${html}`; const parsed = new DOMParser().parseFromString(html, 'text/html'); function flattenNonLiElements(node) { for (const child of node.childNodes) { // 先递归处理子节点(深度优先) flattenNonLiElements(child); // 仅对元素节点进行判断和替换 if (child.nodeType !== Node.ELEMENT_NODE) continue; // 仅保留
✅ 关键要点总结:
)内含嵌套 HTML(如 ),它们也会被一并展开——这正是需求所要求的“只留文本”效果;如需保留特定内联标签,需额外白名单逻辑;
该
