queryselectorall()返回静态nodelist,一次性获取所有匹配元素;支持复杂css选择器,但不自动更新动态插入元素,需手动处理shadow dom。

用 querySelectorAll 一次性获取所有匹配元素最省事
多数时候你并不需要“遍历树”,而是想拿到符合某类条件的元素集合。querySelectorAll 内部已由浏览器优化,比手写递归快得多,且天然支持复杂选择器(如 "div > .item[data-id='123']")。
常见错误是误以为它返回数组——实际是 NodeList,不能直接调用 map 或 forEach(老版本 IE 不支持 forEach),需转成数组或用 for...of:
const items = document.querySelectorAll('.card');
for (const el of items) {
el.classList.add('processed');
}
注意:如果页面后续动态插入新元素,querySelectorAll 不会自动更新结果,它是一次性快照。
需要深度优先遍历?用 element.children 而非 element.childNodes
childNodes 包含文本节点、注释节点,容易在遍历时意外命中空白文本(比如换行缩进),导致 el.nodeType !== 1 判断冗余;而 children 只返回元素节点,更干净。
典型场景:查找某个容器内所有带 data-role="tooltip" 的子元素,不管嵌套几层:
function findAllTooltips(root) {
const result = [];
function walk(el) {
if (el.dataset.role === 'tooltip') result.push(el);
for (const child of el.children) walk(child);
}
walk(root);
return result;
}
- 递归深度大时可能栈溢出,超深 DOM 建议改用栈模拟(
Array存待处理节点) - 避免用
getElementsByTagName('*')—— 它返回实时集合,每次访问都重新计算,性能差
遇到 Shadow DOM 怎么办?shadowRoot 必须显式进入
现代组件库(如 Lit、Stencil)常封装 Shadow DOM,querySelectorAll 默认查不到里面的内容。必须手动检查并递进:
function deepQuery(root, selector) {
const result = [...root.querySelectorAll(selector)];
if (root.shadowRoot) {
result.push(...root.shadowRoot.querySelectorAll(selector));
}
for (const el of root.children) {
result.push(...deepQuery(el, selector));
}
return result;
}
关键点:
-
el.shadowRoot可能为null(closed mode 下不可访问) - 不能依赖
el.getRootNode()直接查,它返回的是根节点,不提供查询接口 - 若只查 open mode 的 Shadow DOM,上面逻辑够用;closed mode 需组件自身暴露方法
为什么不用 document.evaluate?XPath 太重且兼容性有坑
虽然 document.evaluate 支持 XPath 表达式(如 //button[@type='submit']),但实际开发中极少必要:
- IE11 支持但语法细节有差异(比如命名空间处理)
- 移动端 WebView 兼容不稳定,部分安卓低版本会抛
NS_ERROR_FAILURE - 可读性差,调试困难;现代 CSS 选择器基本覆盖全部常用需求
唯一适合它的场景是:必须按文本内容定位(如 //*[text()='提交']),而 textContent 查找又无法精确匹配结构。此时宁可用 TreeWalker + 文本过滤,也比 XPath 更可控。
真正难的不是“怎么遍历”,而是想清楚:目标是否真的散落在任意深度?有没有更稳的锚点(比如固定父容器 ID)?过度泛化遍历逻辑,往往掩盖了 DOM 结构本身的设计问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











