应优先使用 querySelectorAll 而非 getElementsByClassName 等动态集合方法,校验元素存在性,慎用新伪类并检测支持性,缓存查询结果以提升性能。

避免使用已废弃的 DOM 查询方法
早期的 document.getElementsByClassName、document.getElementsByName 返回的是 HTMLCollection(动态集合),而 document.querySelectorAll 返回的是 NodeList(静态快照)。很多人误以为它们行为一致,结果在循环中增删元素时出错。例如:
-
getElementsByClassName每次访问都会重新查询 DOM,若在 for 循环中删除匹配元素,可能跳过下一个节点; -
querySelectorAll返回的是快照,适合遍历修改,更安全可靠; - 完全弃用
document.all和已移除的document.body.children[0]这类非标准写法。
注意选择器作用域与上下文范围
用 element.querySelector 时,查找范围仅限该元素的后代,但新手常误以为会从整个文档找。更隐蔽的问题是:如果传入的 element 是 null 或未挂载节点,调用会直接报错。
- 始终校验父节点是否存在:
if (container && container.querySelector) { ... }; - 避免链式调用导致的“Cannot read property 'querySelector' of null”;
- 需要查兄弟节点?
element.parentElement?.querySelectorAll(...)更稳妥。
警惕伪类与浏览器兼容性陷阱
:nth-child、:has() 等伪类看似方便,但支持度差异大。:has() 直到 Chrome 105+、Firefox 121+ 才稳定支持,Safari 15.4+ 仅部分支持。生产环境盲目使用会导致查询失败返回 null。
- 简单场景优先用
data-*属性 +[data-role="submit"],可控且兼容; - 必须用伪类时,先检测支持性:
CSS.supports('selector(:has(div))'); -
:focus-visible在旧版 Safari 中不生效,别用来做关键逻辑判断。
性能与内存:别让查询成为瓶颈
高频操作(如 scroll、input、动画帧)中反复调用 document.querySelector 是常见性能杀手。DOM 查询本身开销不大,但频繁触发样式计算和重排会拖慢渲染。
- 查询结果尽量缓存,尤其是静态结构:
const header = document.querySelector('header');放在初始化阶段; - 用
Element.closest()替代向上遍历父节点,语义清晰且性能更好; - 监听事件委托时,用
event.target.matches('.btn')判断来源,比每次 query 更轻量。


















