2022 年 10 月 14 日

搜索:getElement*、querySelector*

当元素彼此相邻时,DOM 导航属性非常有用。如果它们不相邻呢?如何获取页面的任意元素?

为此,还有其他搜索方法。

document.getElementById 或仅 id

如果一个元素具有 id 属性,我们可以使用 document.getElementById(id) 方法获取该元素,无论它在哪里。

例如

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // get the element
  let elem = document.getElementById('elem');

  // make its background red
  elem.style.background = 'red';
</script>

此外,有一个名为 id 的全局变量引用该元素

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // elem is a reference to DOM-element with id="elem"
  elem.style.background = 'red';

  // id="elem-content" has a hyphen inside, so it can't be a variable name
  // ...but we can access it using square brackets: window['elem-content']
</script>

…除非我们声明一个同名的 JavaScript 变量,那么该变量优先

<div id="elem"></div>

<script>
  let elem = 5; // now elem is 5, not a reference to <div id="elem">

  alert(elem); // 5
</script>
请不要使用 id 命名全局变量来访问元素

此行为在 规范中进行了描述,但主要出于兼容性考虑提供支持。

浏览器尝试通过混合 JS 和 DOM 的命名空间来帮助我们。对于内联到 HTML 中的简单脚本来说,这很好,但通常不是一件好事。可能会出现命名冲突。此外,当一个人阅读 JS 代码并且没有查看 HTML 时,很难看出变量来自何处。

在本教程中,当元素的来源很明显时,我们使用 id 直接引用元素以简化内容。

在实际应用中,document.getElementById 是首选方法。

id 必须唯一

id 必须唯一。文档中只能有一个具有给定 id 的元素。

如果有多个元素具有相同的 id,那么使用它的方法的行为是不可预测的,例如 document.getElementById 可能会随机返回任何此类元素。因此,请遵守规则并保持 id 唯一。

document.getElementById,不使用 anyElem.getElementById

getElementById 方法只能在 document 对象上调用。它在整个文档中查找给定的 id

querySelectorAll

到目前为止,最通用的方法是 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 只查找一个。因此,它更快,而且更简洁。

matches

以前的方法是搜索 DOM。

elem.matches(css) 不会搜索任何内容,它只是检查 elem 是否与给定的 CSS 选择器匹配。它返回 truefalse

当我们遍历元素(如数组或其他内容)并尝试筛选出我们感兴趣的元素时,该方法非常有用。

例如

<a href="http://example.com/file.zip">...</a>
<a href="http://ya.ru">...</a>

<script>
  // can be any collection instead of document.body.children
  for (let elem of document.body.children) {
    if (elem.matches('a[href$="zip"]')) {
      alert("The archive reference: " + elem.href );
    }
  }
</script>

closest

元素的祖先是:父级、父级的父级、父级的父级,依此类推。祖先共同构成了从元素到顶部的父级链。

方法 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 (because h1 is not an ancestor)
</script>

getElementsBy*

还有其他方法可以通过标签、类等查找节点。

如今,它们基本上已成为历史,因为 querySelector 更强大,而且编写起来更简洁。

因此,我们主要出于完整性的考虑在此介绍它们,而你仍然可以在旧脚本中找到它们。

  • elem.getElementsByTagName(tag) 查找具有给定标签的元素并返回它们的集合。tag 参数也可以是星号 "*",表示“任何标签”。
  • elem.getElementsByClassName(className) 返回具有给定 CSS 类的元素。
  • document.getElementsByName(name) 返回具有给定 name 属性的元素,范围为整个文档。使用得很少。

例如

// get all divs in the document
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"

新手开发者有时会忘记字母 "s"。也就是说,他们尝试调用 getElementByTagName 而不是 getElementsByTagName

getElementById 中没有字母 "s",因为它返回单个元素。但 getElementsByTagName 返回元素的集合,所以里面有 "s"

它返回一个集合,而不是一个元素!

另一个常见的初学者错误是编写

// doesn't work
document.getElementsByTagName('input').value = 5;

这不起作用,因为它获取输入的集合并将其值分配给它,而不是分配给其中的元素。

我们应该遍历集合或按其索引获取元素,然后进行分配,如下所示

// should work (if there's an input)
document.getElementsByTagName('input')[0].value = 5;

寻找 .article 元素

<form name="my-form">
  <div class="article">Article</div>
  <div class="long article">Long article</div>
</form>

<script>
  // find by name attribute
  let form = document.getElementsByName('my-form')[0];

  // find by class inside the form
  let articles = form.getElementsByClassName('article');
  alert(articles.length); // 2, found two elements with class "article"
</script>

实时集合

所有 "getElementsBy*" 方法都会返回一个实时集合。此类集合始终反映文档的当前状态,并在文档发生更改时“自动更新”。

在下面的示例中,有两个脚本。

  1. 第一个脚本创建对 <div> 集合的引用。截至目前,其长度为 1
  2. 第二个脚本在浏览器遇到另一个 <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 返回一个静态集合。它就像一个固定元素数组。

如果我们改用它,那么两个脚本都会输出 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 后,静态集合没有增加。

摘要

有 6 种主要方法可以在 DOM 中搜索节点

方法 按...搜索 可以在元素上调用吗? 实时?
querySelector CSS 选择器 -
querySelectorAll CSS 选择器 -
getElementById id - -
getElementsByName 名称 -
getElementsByTagName 标签或 '*'
getElementsByClassName

到目前为止,使用最多的方法是 querySelectorquerySelectorAll,但 getElement(s)By* 偶尔会有帮助,或在旧脚本中找到。

除此之外

  • elem.matches(css) 用于检查 elem 是否与给定的 CSS 选择器匹配。
  • elem.closest(css) 用于查找与给定的 CSS 选择器匹配的最近祖先。elem 本身也会被检查。

我们再提一下另一种方法来检查子父关系,因为它有时很有用

  • elemA.contains(elemB) 如果 elemBelemAelemA 的后代)中或 elemA==elemB,则返回 true。

任务

重要性:4

以下是包含表格和表单的文档。

如何查找?...

  1. id 为 age-table 的表格。
  2. 该表格中的所有 label 元素(应该有 3 个)。
  3. 该表格中的第一个 td(包含单词“年龄”)。
  4. 名称为 searchform
  5. 该表单中的第一个 input
  6. 该表单中的最后一个 input

在单独的窗口中打开页面 table.html,并为此使用浏览器工具。

有很多方法可以做到这一点。

以下是一些方法

// 1. The table with `id="age-table"`.
let table = document.getElementById('age-table')

// 2. All label elements inside that table
table.getElementsByTagName('label')
// or
document.querySelectorAll('#age-table label')

// 3. The first td in that table (with the word "Age")
table.rows[0].cells[0]
// or
table.getElementsByTagName('td')[0]
// or
table.querySelector('td')

// 4. The form with the name "search"
// assuming there's only one element with name="search" in the document
let form = document.getElementsByName('search')[0]
// or, form specifically
document.querySelector('form[name="search"]')

// 5. The first input in that form.
form.getElementsByTagName('input')[0]
// or
form.querySelector('input')

// 6. The last input in that form
let inputs = form.querySelectorAll('input') // find all inputs
inputs[inputs.length-1] // take the last one
教程地图

评论

在评论之前先阅读此内容...
  • 如果您有改进建议 - 请 提交 GitHub 问题 或提交拉取请求,而不是发表评论。
  • 如果您无法理解文章中的某些内容 - 请详细说明。
  • 要插入几行代码,请使用 <code> 标记,对于多行 - 将其包装在 <pre> 标记中,对于超过 10 行的内容 - 使用沙箱 (plnkrjsbincodepen...)