- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
本文是 Modern JavaScript Tutorial(en.javascript.info)中 "Searching: getElement*, querySelector*" 章节的深度技术解析。文章围绕document.getElementById、querySelectorAll、querySelector、matches、closest与getElementsBy*六大搜索方法展开,并结合仓库内配套练习 1-find-elements/task.md 与真实页面 table.html 演示实战用法。读完本文,你将能根据id、CSS 选择器、标签名、类名或name属性精准定位任意 DOM 元素,并理解"活集合(live collection)"与"静态集合"的本质差异。
为什么需要"搜索"方法
在前一章 Walking the DOM(03-dom-navigation) 中我们了解到,DOM 允许我们通过childNodes、parentNode、nextSibling等导航属性在节点之间"行走"。但这种导航方式只在元素彼此相邻时高效:要拿到页面中任意一个元素,从document出发沿着导航属性一步步走过去既不现实也不可维护。
正如 browser environment(01-browser-environment) 所介绍的,document对象是页面的主要"入口点"。DOM 提供了另一组搜索方法(searching methods),让我们可以绕过导航属性,直接按特征(id、CSS 选择器、标签、类名、name)抓取任意元素。这就是本文的主题:getElement*与querySelector*两大家族。
document.getElementById:按 id 精准定位
如果一个元素带有id属性,无论它在文档中的哪个位置,都可以通过document.getElementById(id)拿到它:
<div id="elem"> <div id="elem-content">Element</div> </div> <script> // 获取元素 let elem = document.getElementById('elem'); // 将其背景设为红色 elem.style.background = 'red'; </script>getElementById是定位单个元素最直接、性能最优的手段之一,在真实项目中它也是官方推荐的取元素方式。
名为 id 的全局变量
浏览器还提供了一种"快捷方式":文档中具有id的元素会同时生成一个同名的全局变量,直接引用该 DOM 元素:
<div id="elem"> <div id="elem-content">Element</div> </div> <script> // elem 直接引用了 id="elem" 的 DOM 元素 elem.style.background = 'red'; // id="elem-content" 含连字符,不能作为变量名 // ……但可以用方括号通过 window['elem-content'] 访问 </script>不过,如果我们在 JavaScript 中声明了同名变量,它就会覆盖这个全局引用:
<div id="elem"></div> <script> let elem = 5; // 现在 elem 是 5,不再是 <div id="elem"> 的引用 alert(elem); // 5 </script>不要在实战中使用"以 id 命名的全局变量"
该行为在 HTML 规范 中有描述,但主要是为了兼容旧代码。浏览器试图把 JS 命名空间和 DOM 命名空间混合起来"帮"我们,这对内联在 HTML 里的简单脚本没问题,但通常不是好做法:可能存在命名冲突;而且当读者只看 JS 代码、看不到 HTML 时,很难判断这个变量从何而来。
教程里为了简洁会直接用
id引用元素,但真实项目中document.getElementById才是首选方法。
id必须是唯一的一个文档中只能有一个元素拥有给定的
id。如果出现多个相同id,使用它的方法行为将不可预测——例如document.getElementById可能随机返回其中任意一个。请务必遵守"id唯一"的规则。
只能在
document上调用getElementById该方法只能作用于
document对象,它会在整个文档中查找给定的id。不存在anyElem.getElementById这种写法。
querySelectorAll:最强通用的 CSS 选择器搜索
elem.querySelectorAll(css)是用途最广的方法:返回elem内部所有匹配给定 CSS 选择器的元素。
例如,找出所有作为最后一个子元素的<li>:
<ul> <li>The</li> <li>test</li> </ul> <ul> <li>has</li> <li>passed</li> </ul> <script> let elements = document.querySelectorAll('ul > li:last-child'); for (let elem of elements) { alert(elem.innerHTML); // "test", "passed" } </script>这个方法之所以强大,是因为任何 CSS 选择器都可以用:类型选择器、类选择器、属性选择器、后代组合、子组合、兄弟组合等一应俱全。
伪类同样支持
CSS 选择器中的伪类(如
:hover、:active)也受支持。例如document.querySelectorAll(':hover')会返回当前指针悬停之上的元素集合(按嵌套顺序,从最外层的<html>到最内层)。
querySelector:只取第一个匹配项
elem.querySelector(css)返回给定 CSS 选择器的第一个匹配元素。
换句话说,它的结果等价于elem.querySelectorAll(css)[0],但后者会先找出全部匹配元素再取其一,而elem.querySelector只查找一个。因此它更快,写起来也更短。
在仓库配套练习的解法 solution.md 中,可以清晰看到这种替代关系:
// 5. 表单中的第一个 input form.getElementsByTagName('input')[0] // 或者 form.querySelector('input')当只需要一个元素时,querySelector是比getElementsByTagName(...)[0]更简洁、更现代的选择。
matches:只判断、不搜索
前面几种方法都在"搜索" DOM,而elem.matches(css)不做任何查找,它只检查elem是否匹配给定的 CSS 选择器,返回true或false。
当我们遍历元素集合(例如数组或document.body.children)并想筛选出感兴趣的元素时,这个方法非常方便:
<a href="http://example.com/file.zip">...</a> <a href="http://ya.ru">...</a> <script> // 这里可以是任何集合,不限于 document.body.children for (let elem of document.body.children) { if (elem.matches('a[href$="zip"]')) { alert("The archive reference: " + elem.href); } } </script>上例中a[href$="zip"]是属性选择器,$=表示"以……结尾",因此能精确筛出指向.zip压缩包的链接。matches常与事件委托配合使用(在e.target上判断点击来源),是 DOM 编程中的高频工具。
closest:向祖先链向上查找
元素的**祖先(ancestors)**包括:父元素、父元素的父元素,依此类推,最终连成从该元素到文档顶端的链条。
elem.closest(css)查找最近的、匹配给定 CSS 选择器的祖先。注意:elem自身也包含在查找范围内。
也就是说,closest从元素出发逐级向上检查每个父节点,一旦某级匹配选择器,搜索立即停止并返回该祖先:
<h1>Contents</h1> <div class="contents"> <ul class="book"> <li class="chapter">Chapter 1</li> <li class="chapter">Chapter 2</li> </ul> </div> <script> let chapter = document.querySelector('.chapter'); // LI alert(chapter.closest('.book')); // UL alert(chapter.closest('.contents')); // DIV alert(chapter.closest('h1')); // null(h1 不是祖先) </script>closest是处理嵌套结构(如菜单、列表、卡片)中"点击了哪个容器"这类问题的利器,与事件委托配合可以大大简化代码。
getElementsBy*:按标签、类名、name 搜索
此外还有一组按标签、类名等查找节点的方法。如今它们大多属于"历史遗留",因为querySelector更强大且书写更短,这里讲解它们主要为了完整性——你仍会在旧脚本中见到它们:
elem.getElementsByTagName(tag)—— 按给定标签查找元素并返回集合;tag参数也可以是星号"*",表示"任意标签"。elem.getElementsByClassName(className)—— 返回拥有给定 CSS 类的元素。document.getElementsByName(name)—— 按name属性在整个文档范围内查找元素,极少使用。
基础用法:
// 获取文档中所有 div let divs = document.getElementsByTagName('div');在指定元素内部继续搜索也同样自然。下面示例查找表格中的所有input标签:
<table id="table"> <tr> <td>Your age:</td> <td> <label> <input type="radio" name="age" value="young" checked> less than 18 </label> <label> <input type="radio" name="age" value="mature"> from 18 to 50 </label> <label> <input type="radio" name="age" value="senior"> more than 60 </label> </td> </tr> </table> <script> let inputs = table.getElementsByTagName('input'); for (let input of inputs) { alert( input.value + ': ' + input.checked ); } </script>常见坑一:别忘字母 "s"!
新手常把getElement**s**ByTagName写成getElementByTagName。规律如下:
getElementById中没有"s",因为它只返回单个元素;getElementsByTagName返回元素集合,所以里面有"s"。
常见坑二:返回的是集合,不是元素!
另一个普遍错误是试图直接给集合赋值:
// 不生效 document.getElementsByTagName('input').value = 5;这样写不会生效,因为拿到的是一组 input 的集合,赋值被施加在集合本身而非集合内的元素上。正确做法是遍历集合,或先按下标取出元素再赋值:
// 正确(假设存在 input) document.getElementsByTagName('input')[0].value = 5;按 name 与按 class 的组合示例
下面的例子同时演示了getElementsByName(文档级)与getElementsByClassName(元素级):
<form name="my-form"> <div class="article">Article</div> <div class="long article">Long article</div> </form> <script> // 按 name 属性查找 let form = document.getElementsByName('my-form')[0]; // 在 form 内部按 class 查找 let articles = form.getElementsByClassName('article'); alert(articles.length); // 2,找到两个 class 含 "article" 的元素 </script>活集合(Live collections)与静态集合
所有getElementsBy*方法返回的都是活集合(live collection):这类集合始终反映文档的当前状态,当文档变化时会"自动更新"。
下面例子中有两段脚本:
- 第一段创建了对
<div>集合的引用,此时其长度为1; - 浏览器解析到又一个
<div>后运行的第二段脚本看到长度为2。
<div>First div</div> <script> let divs = document.getElementsByTagName('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 2 </script>与之相反,querySelectorAll返回静态集合(static collection),它就像元素的一个固定数组。若换用它,两段脚本输出的都是1:
<div>First div</div> <script> let divs = document.querySelectorAll('div'); alert(divs.length); // 1 </script> <div>Second div</div> <script> alert(divs.length); // 1 </script>现在差异一目了然:文档中出现新的div后,静态集合并没有随之增长。这一特性在动态页面中尤为关键——如果你先取得集合、之后 DOM 又发生变化,getElementsBy*的活集合会立即反映新增/删除的节点,而querySelectorAll的快照则保持不变。
实战演练:查找表格与表单中的元素
仓库为本章配有一道完整练习 1-find-elements/task.md,其页面 table.html 同时包含表单与表格结构。练习要求完成六项查找:
id="age-table"的表格;- 该表格内所有
label元素(共 3 个); - 表格内第一个
td(含 "Age" 字样); name="search"的表单;- 该表单中的第一个
input; - 该表单中的最后一个
input。
官方解法 solution.md 展示了每种目标的多条可行路径,恰好覆盖了本文全部方法:
// 1. id="age-table" 的表格 let table = document.getElementById('age-table') // 2. 表格内所有 label 元素 table.getElementsByTagName('label') // 或 document.querySelectorAll('#age-table label') // 3. 表格内第一个 td(含 "Age" 字样) table.rows[0].cells[0] // 或 table.getElementsByTagName('td')[0] // 或 table.querySelector('td') // 4. name="search" 的表单 // 假设文档中只有一个 name="search" 的元素 let form = document.getElementsByName('search')[0] // 或,专门针对表单 document.querySelector('form[name="search"]') // 5. 表单中的第一个 input form.getElementsByTagName('input')[0] // 或 form.querySelector('input') // 6. 表单中的最后一个 input let inputs = form.querySelectorAll('input') // 找到全部 input inputs[inputs.length - 1] // 取最后一个注意第 3 问中table.rows[0].cells[0]用到了表格特有的rows/cells集合属性,这是getElementsBy*之外的另一种快捷路径。可见,同一查找目标往往存在多种等价实现,理解每种方法的特性(是否活集合、是否支持复杂选择器、调用对象限制)才能选出最合适的方案。
总结
DOM 中搜索节点共有6 个主要方法:
| 方法 | 按什么搜索 | 能否在元素上调用 | 是否活集合 |
|---|---|---|---|
querySelector | CSS 选择器 | ✔ | - |
querySelectorAll | CSS 选择器 | ✔ | - |
getElementById | id | - | - |
getElementsByName | name | - | ✔ |
getElementsByTagName | 标签或'*' | ✔ | ✔ |
getElementsByClassName | class | ✔ | ✔ |
使用频率最高的是querySelector与querySelectorAll,但getElement(s)By*在特定场景仍有用武之地,也常见于旧脚本。
除此之外还有两个高频辅助方法:
elem.matches(css)—— 检查elem是否匹配给定 CSS 选择器;elem.closest(css)—— 查找最近的、匹配给定 CSS 选择器的祖先,elem自身也会被检查。
最后再补充一个用于判断父子关系的方法(有时很实用):
elemA.contains(elemB)—— 若elemB在elemA内部(是elemA的后代),或elemA === elemB,返回true。
至此,从getElementById的单点精准定位,到querySelectorAll的任意 CSS 选择器批量抓取,再到matches/closest的关系判断,你已经掌握了在 document 章节(2-ui/1-document) 后续内容中操作页面元素的全部"定位武器"。下一章 basic-dom-node-properties(05-basic-dom-node-properties) 将进一步介绍这些元素节点自身的属性体系。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
JavaScript教程:DOM元素搜索方法详解
JavaScript教程:DOM元素搜索方法详解 在Web开发中,DOM元素搜索是JavaScript操作页面元素的基础。本文将全面介绍JavaScript中常
文档教程前端JavaScript DOM 元素搜索方法详解
JavaScript DOM 元素搜索方法详解 前言 在 JavaScript 中操作 DOM 时,查找元素是最基础也是最重要的操作之一。本文将全面介绍 Jav
文档/教程前端JavaScript DOM 元素搜索方法详解
JavaScript DOM 元素搜索方法详解 前言 在 JavaScript 中操作网页元素时,首先需要找到这些元素。DOM 提供了多种搜索元素的方法,每种方
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考