跟着 MDN 学 HTML day_28:(使用选择器 API 在 DOM 树中进行选择与遍历)
引言在早期的前端开发中要通过 JavaScript 获取页面中的特定元素往往需要编写循环遍历的代码或者依赖 getElementById、getElementsByClassName 等方法进行多次调用。随着 Selectors API 的引入开发者可以直接使用 CSS 选择器语法来查询 DOM 树中的元素这使得元素查找变得更加高效和直观。本文将详细介绍 querySelector 和 querySelectorAll 这两个核心方法以及使用选择器时需要注意的细节。一、NodeSelector 接口与两个核心方法Selectors API 为所有实现了 Document、DocumentFragment 和 Element 接口的对象添加了两个新方法。这意味着不仅可以在整个文档中进行查询还可以在特定的文档片段或元素子树中进行局部查询。dividcontainerpclassintro第一段介绍文字/ppclasscontent第二段正文内容/pspanclasscontent这是一个 span 元素/span/divscript// querySelector 返回第一个匹配的元素constfirstContentdocument.querySelector(.content);console.log(firstContent.textContent);// 第二段正文内容p 元素在前// 如果没有匹配的元素返回 nullconstnotFounddocument.querySelector(.nonexistent);console.log(notFound);// null// querySelectorAll 返回所有匹配的元素constallContentdocument.querySelectorAll(.content);console.log(allContent.length);// 2p 和 spanallContent.forEach(el{console.log(el.tagName,el.textContent);});// P 第二段正文内容// SPAN 这是一个 span 元素// 如果没有匹配的元素返回空的 NodeListconstemptyResultdocument.querySelectorAll(.nonexistent);console.log(emptyResult.length);// 0/scriptquerySelector 始终返回第一个匹配的元素如果查找目标是唯一元素如通过 ID 查询这个方法是最高效的选择。querySelectorAll 则返回所有匹配元素的集合适合批量操作场景。二、在特定子树中进行查询查询方法可以基于任意元素节点调用而不仅仅局限于 document 对象。这样可以将搜索范围限制在特定的 DOM 子树中提高查询效率并避免意外匹配到范围外的元素。articleidpost-1h2classtitle文章一的标题/h2pclasssummary文章一的摘要/p/articlearticleidpost-2h2classtitle文章二的标题/h2pclasssummary文章二的摘要/p/articlescriptconstpost1document.getElementById(post-1);constpost2document.getElementById(post-2);// 在 post-1 的子树中查询不会匹配到 post-2 的元素consttitleInPost1post1.querySelector(.title);console.log(titleInPost1.textContent);// 文章一的标题constsummaryInPost2post2.querySelector(.summary);console.log(summaryInPost2.textContent);// 文章二的摘要// 子树查询不会包含调用节点本身只搜索其子节点// 下面的查询在 post-1 内部查找 article 元素constarticleInsidepost1.querySelector(article);console.log(articleInside);// nullpost-1 本身不会被匹配/script在复杂页面中限定查询范围可以显著减少浏览器需要遍历的节点数量。当处理动态加载的内容或组件时在组件根元素上调用查询方法是一种良好的实践。三、querySelectorAll 返回的是静态 NodeList与其他 DOM 方法如 getElementsByClassName返回的动态 HTMLCollection 不同querySelectorAll 返回的是一个静态的 NodeList。这意味着获取结果后后续对 DOM 的修改不会自动反映到这个列表中。ulidtask-listliclasstask任务一/liliclasstask任务二/li/ulbuttonidadd-task添加任务/buttonbuttonidcheck-static检查静态列表/buttonscriptconsttaskListdocument.getElementById(task-list);constaddBtndocument.getElementById(add-task);constcheckBtndocument.getElementById(check-static);// 通过 querySelectorAll 获取静态 NodeListconststaticTasksdocument.querySelectorAll(.task);console.log(初始数量,staticTasks.length);// 2// 通过 getElementsByClassName 获取动态 HTMLCollectionconstliveTasksdocument.getElementsByClassName(task);console.log(动态集合初始数量,liveTasks.length);// 2addBtn.addEventListener(click,(){constnewTaskdocument.createElement(li);newTask.classNametask;newTask.textContent任务${taskList.children.length1};taskList.appendChild(newTask);console.log(添加后动态集合数量,liveTasks.length);// 3自动更新});checkBtn.addEventListener(click,(){console.log(静态列表数量,staticTasks.length);// 始终为 2// 需要重新查询才能获取最新的元素constrefresheddocument.querySelectorAll(.task);console.log(重新查询后数量,refreshed.length);});/script静态特性有时是优点如果需要在遍历 NodeList 的同时修改 DOM 结构静态列表可以避免因集合动态变化而导致的遍历问题。但如果需要始终反映最新的 DOM 状态则需要重新执行查询。四、灵活使用选择器列表两个查询方法都支持选择器列表可以在一次调用中使用多个逗号分隔的选择器匹配其中任意一个的元素都会被返回。dividdashboardbuttonclassprimary主要操作/buttonbuttonclasssecondary次要操作/buttonaclassprimaryhref/more了解更多/aspanclasssecondary提示信息/span/divscript// 通过逗号分隔的选择器列表一次性匹配多种元素constclickableElementsdocument.querySelectorAll(button.primary, a.primary, button.secondary);console.log(可交互元素数量,clickableElements.length);// 3clickableElements.forEach(el{console.log(el.tagName,el.className);});// BUTTON primary// BUTTON secondary// A primary// 选择器列表在 querySelector 中同样适用// 返回按文档顺序第一个匹配的元素constfirstInteractivedocument.querySelector(span.secondary, a.primary, button.primary);console.log(firstInteractive.tagName,firstInteractive.className);// BUTTON primary按照文档顺序button.primary 先出现/script选择器列表中的每个选择器都是独立匹配的最终的排列顺序由元素在文档中的先后位置决定而非选择器在列表中的书写顺序。这一特性在需要同时关注多种类型元素时非常实用。五、通过 ID 进行查询使用选择器 API 可以通过 ID 选择器来查找元素甚至可以一次传入多个 ID 选择器返回第一个存在于文档中的匹配元素。dividsection-a这是 A 区域/div!-- 注意文档中没有 idsection-b 的元素 --dividsection-c这是 C 区域/divscript// 单个 ID 查询constsectionAdocument.querySelector(#section-a);console.log(sectionA.textContent);// 这是 A 区域// 使用多个 ID 选择器返回第一个在文档中存在的元素constfirstSectiondocument.querySelector(#section-b, #section-c, #section-a);console.log(firstSection.textContent);// 这是 C 区域section-c 在文档顺序中先于 section-a 出现且存在// 如果所有 ID 都不存在返回 nullconstmissingdocument.querySelector(#nonexistent-1, #nonexistent-2);console.log(missing);// null// 相比 getElementById 的优势可以一次尝试多个 ID// 等效的传统写法需要多次调用和条件判断consteldocument.getElementById(section-b)||document.getElementById(section-c)||document.getElementById(section-a);console.log(el.textContent);// 这是 C 区域/script使用多 ID 选择器可以简化一些防御性编程场景当不确定页面上是否存在某个 ID 时可以按优先级列出备选 ID浏览器会返回第一个存在的元素。这比手动编写多个 getElementById 调用的回退逻辑更加简洁。六、伪类选择器的限制出于隐私保护和安全性考虑选择器 API 对某些伪类选择器的支持有限制。特别是 :visited 伪类不会返回任何匹配结果而 :link 会被当作 :any-link 处理。navahrefhttps://example.comclassexternal外部链接未访问/aahrefhttps://developer.mozilla.orgclassexternalMDN 链接/aahref/localclasslocal内部链接/a/navscript// :link 伪类会被当作 :any-link 处理 const allLinks document.querySelectorAll(a:link); console.log(匹配到的链接数量, allLinks.length); // 可能匹配所有带有 href 属性的 a 元素 // :visited 伪类不会返回任何匹配结果 const visitedLinks document.querySelectorAll(a:visited); console.log(visited 匹配数量, visitedLinks.length); // 0始终为空 // 这种限制防止了恶意网站通过检测链接样式来窥探用户浏览历史 // 在开发中应避免依赖 :visited 进行元素选择 // 可以正常使用的结构伪类 const firstLink document.querySelector(a:first-of-type); console.log(firstLink.textContent); // 外部链接未访问 const noHrefLinks document.querySelectorAll(a:not([href])); console.log(没有 href 的链接数量, noHrefLinks.length);lt;/scriptgt;了解这些限制可以帮助开发者避免在元素查询中编写无效的选择器。如果需要根据链接状态来改变行为应该在 CSS 层面处理样式在 JavaScript 层面通过其他数据属性来标记状态。总结Selectors API 是现代 Web 开发中进行 DOM 查询的基石它让元素查找变得像编写 CSS 一样自然。本文涵盖的核心要点包括querySelector 返回子树中第一个匹配的元素未找到时返回 null。querySelectorAll 返回所有匹配元素的静态 NodeList未找到时返回空列表。这两个方法可以在 Document、DocumentFragment 和 Element 上调用支持在特定子树中限定查询范围。返回的 NodeList 是静态的DOM 的后续变化不会自动反映到已获取的集合中。选择器列表允许在一次调用中组合多个选择器匹配顺序由文档结构而非选择器书写顺序决定。多 ID 选择器可以简化需要回退查找的场景。出于隐私保护:visited 伪类始终返回空结果:link 会被当作 :any-link 处理。掌握这些知识后开发者可以告别冗长的循环遍历代码用简洁高效的选择器语法精确地定位到目标元素。想要解锁更多HTML 核心标签实战、前端零基础入门干货、开发避坑全指南吗持续关注后续将更新CSS 布局实战、JavaScript 交互基础、全站导航开发等硬核内容带你从新手快速进阶轻松搞定前端开发