HTML解析引擎不提供遍历API,实际操作的是已构建的DOM树;querySelectorAll仅查询现有DOM树,非重解析;TreeWalker是唯一原生支持可控遍历的接口;递归遍历易栈溢出,应使用显式栈+类型过滤。

HTML 解析引擎本身不提供遍历 API,你实际要操作的是浏览器已构建好的 DOM 树,不是“解析过程”本身。 所有主流浏览器在加载 HTML 后,都会生成一棵内存中的 DOM 树,document 就是它的根。所谓“遍历页面节点”,本质是用 JavaScript 在这棵树上导航、筛选、读取——和 HTML 解析器(如 Chrome 的 Blink、Firefox 的 Gecko 内部解析器)无关,那些组件早已完成工作并退场。
为什么 document.querySelectorAll 不等于 HTML 解析引擎遍历
很多人误以为 querySelectorAll 是在“调用解析器重新解析”,其实它只是对已存在的 DOM 树做快速匹配查询:
-
querySelectorAll("div p")不会触发 HTML 重解析,只走 DOM 索引或树遍历路径 - 它返回的是
NodeList,不是字符串或 token 流,说明目标已是结构化节点对象 - 若 HTML 还没加载完就调用,结果为空或不全——因为 DOM 树尚未建好,不是解析器没启动
- 深度选择器如
"section > article > header > h1"在某些旧引擎中可能隐式递归,但这是实现细节,不是你该依赖的行为
TreeWalker 是最接近“可控解析式遍历”的标准接口
如果你需要按类型、按条件、跳过子树地逐个访问节点(比如只取非空文本 + 指定 class 的元素),document.createTreeWalker() 是唯一原生支持“游标式推进 + 节点级过滤”的方案:
- 必须传入一个真实 DOM 节点(如
document.body)作为根,不能传 HTML 字符串 -
whatToShow参数是位掩码,常用组合:NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT -
acceptNode方法里,return NodeFilter.FILTER_SKIP表示跳过当前节点但继续遍历其子节点;FILTER_REJECT表示彻底剔除该节点及其全部后代——这点极易误用 - 不要在
acceptNode中直接访问node.classList,先加if (!(node instanceof HTMLElement)) return NodeFilter.FILTER_SKIP;,否则遇到文本节点会报错
深层嵌套遍历时,递归函数大概率崩溃,别硬刚
写一个 function traverse(node) { node.childNodes.forEach(traverse); } 看似简洁,但在真实页面(尤其 CMS 生成的多层嵌套结构)中极易栈溢出:
立即学习“前端免费学习笔记(深入)”;
- DOM 深度超 20 层很常见,Chrome 主线程调用栈限制约 16–20 层,一触即崩
-
childNodes包含文本节点、注释节点,它们没有children却占栈帧,纯属浪费 - 真正安全的做法是用显式栈 + 类型过滤:
if (node.nodeType !== Node.ELEMENT_NODE) continue; - 业务上几乎不需要遍历超过 6 层的结构,加
if (depth > 6) break;不是妥协,是合理剪枝
最容易被忽略的一点:你拿到的“节点”,90% 情况下不是你要的元素节点——换行、空格、注释都是独立的 Node。不加类型判断就调 node.tagName 或 node.classList,错误来得又快又静默。



















