Javascript 搜索:getElement*,querySelector*

2023-02-17 10:54 更新

當(dāng)元素彼此靠得近時,DOM 導(dǎo)航屬性(navigation property)非常有用。如果不是,那該怎么辦?如何去獲取頁面上的任意元素?

還有其他搜索方法。

document.getElementById 或者只使用 id

如果一個元素有 id 特性(attribute),那我們就可以使用 document.getElementById(id) 方法獲取該元素,無論它在哪里。

例如:

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

<script>
  // 獲取該元素
  let elem = document.getElementById('elem');

  // 將該元素背景改為紅色
  elem.style.background = 'red';
</script>

此外,還有一個通過 id 命名的全局變量,它引用了元素:

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

<script>
  // elem 是對帶有 id="elem" 的 DOM 元素的引用
  elem.style.background = 'red';

  // id="elem-content" 內(nèi)有連字符,所以它不能成為一個變量
  // ...但是我們可以通過使用方括號 window['elem-content'] 來訪問它
</script>

……除非我們聲明一個具有相同名稱的 JavaScript 變量,否則它具有優(yōu)先權(quán):

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

<script>
  let elem = 5; // 現(xiàn)在 elem 是 5,而不是對 <div id="elem"> 的引用

  alert(elem); // 5
</script>

請不要使用以 id 命名的全局變量來訪問元素

在規(guī)范中 對此行為進(jìn)行了描述,所以它是一種標(biāo)準(zhǔn)。但這是注意考慮到兼容性才支持的。

瀏覽器嘗試通過混合 JavaScript 和 DOM 的命名空間來幫助我們。對于內(nèi)聯(lián)到 HTML 中的簡單腳本來說,這還行,但是通常來說,這不是一件好事。因為這可能會造成命名沖突。另外,當(dāng)人們閱讀 JavaScript 代碼且看不到對應(yīng)的 HTML 時,變量的來源就會不明顯。

在本教程中,我們只會在元素來源非常明顯時,為了簡潔起見,才會使用 id 直接引用對應(yīng)的元素。

在實際開發(fā)中,document.getElementById 是首選方法。

?id? 必須是唯一的

id 必須是唯一的。在文檔中,只能有一個元素帶有給定的 id。

如果有多個元素都帶有同一個 id,那么使用它的方法的行為是不可預(yù)測的,例如 document.getElementById 可能會隨機(jī)返回其中一個元素。因此,請遵守規(guī)則,保持 id 的唯一性。

只有 ?document.getElementById?,沒有 ?anyElem.getElementById?

getElementById 方法只能被在 document 對象上調(diào)用。它會在整個文檔中查找給定的 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>

這個方法確實功能強(qiáng)大,因為可以使用任何 CSS 選擇器。

也可以使用偽類

CSS 選擇器的偽類,例如 :hover 和 :active 也都是被支持的。例如,document.querySelectorAll(':hover') 將會返回鼠標(biāo)指針正處于其上方的元素的集合(按嵌套順序:從最外層 <html> 到嵌套最多的元素)。

querySelector

elem.querySelector(css) 調(diào)用會返回給定 CSS 選擇器的第一個元素。

換句話說,結(jié)果與 elem.querySelectorAll(css)[0] 相同,但是后者會查找 所有 元素,并從中選取一個,而 elem.querySelector 只會查找一個。因此它在速度上更快,并且寫起來更短。

matches

之前的方法是搜索 DOM。

elem.matches(css) 不會查找任何內(nèi)容,它只會檢查 elem 是否與給定的 CSS 選擇器匹配。它返回 true 或 false。

當(dāng)我們遍歷元素(例如數(shù)組或其他內(nèi)容)并試圖過濾那些我們感興趣的元素時,這個方法會很有用。

例如:

<a  rel="external nofollow" target="_blank" >...</a>
<a  rel="external nofollow" target="_blank" >...</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>

closest

元素的祖先(ancestor)是:父級,父級的父級,它的父級等。祖先們一起組成了從元素到頂端的父級鏈。

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>

getElementsBy*

還有其他通過標(biāo)簽,類等查找節(jié)點的方法。

如今,它們大多已經(jīng)成為了歷史,因為 querySelector 功能更強(qiáng)大,寫起來更短。

因此,這里我們介紹它們只是為了完整起見,而你仍然可以在舊腳本中找到這些方法。

  • ?elem.getElementsByTagName(tag)? 查找具有給定標(biāo)簽的元素,并返回它們的集合。?tag? 參數(shù)也可以是對于“任何標(biāo)簽”的星號 ?"*"?。
  • ?elem.getElementsByClassName(className)? 返回具有給定CSS類的元素。
  • ?document.getElementsByName(name)? 返回在文檔范圍內(nèi)具有給定 ?name? 特性的元素。很少使用。

例如:

// 獲取文檔中的所有 div
let divs = document.getElementsByTagName('div');

讓我們查找 table 中的所有 input 標(biāo)簽:

<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"?!

新手開發(fā)者有時會忘記字符 "s"。也就是說,他們會調(diào)用 getElementByTagName 而不是 getElementsByTagName

getElementById 中沒有字母 "s",是因為它只返回單個元素。但是 getElementsByTagName 返回的是元素的集合,所以里面有 "s"。

它返回的是一個集合,不是一個元素!

新手的另一個普遍的錯誤是寫:

// 行不通
document.getElementsByTagName('input').value = 5;

這是行不通的,因為它需要的是一個 input 的 集合,并將值賦(assign)給它,而不是賦值給其中的一個元素。

我們應(yīng)該遍歷集合或通過對應(yīng)的索引來獲取元素,然后賦值,如下所示:

// 應(yīng)該可以運行(如果有 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>
  // 按 name 特性查找
  let form = document.getElementsByName('my-form')[0];

  // 在 form 中按 class 查找
  let articles = form.getElementsByClassName('article');
  alert(articles.length); // 2, found two elements with class "article"
</script>

實時的集合

所有的 "getElementsBy*" 方法都會返回一個 實時的(live) 集合。這樣的集合始終反映的是文檔的當(dāng)前狀態(tài),并且在文檔發(fā)生更改時會“自動更新”。

在下面的例子中,有兩個腳本。

  1. 第一個創(chuàng)建了對 ?<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 返回的是一個 靜態(tài)的 集合。就像元素的固定數(shù)組。

如果我們使用它,那么兩個腳本都會輸出 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>

現(xiàn)在我們可以很容易地看到不同之處。在文檔中出現(xiàn)新的 div 后,靜態(tài)集合并沒有增加。

總結(jié)

有 6 種主要的方法,可以在 DOM 中搜索元素節(jié)點:

方法名 搜索方式 可以在元素上調(diào)用? 實時的?
querySelector CSS-selector ? -
querySelectorAll CSS-selector ? -
getElementById id - -
getElementsByName name - ?
getElementsByTagName tag or '*' ? ?
getElementsByClassName class ? ?

目前為止,最常用的是 querySelector 和 querySelectorAll,但是 getElement(s)By* 可能會偶爾有用,或者可以在舊腳本中找到。

此外:

  • ?elem.matches(css)? 用于檢查 ?elem? 與給定的 CSS 選擇器是否匹配。
  • ?elem.closest(css)? 用于查找與給定 CSS 選擇器相匹配的最近的祖先。?elem? 本身也會被檢查。

讓我們在這里提一下另一種用來檢查子級與父級之間關(guān)系的方法,因為它有時很有用:

  • 如果 ?elemB? 在 ?elemA? 內(nèi)(?elemA? 的后代)或者 ?elemA==elemB?,?elemA.contains(elemB)? 將返回 true。

任務(wù)


搜索元素

重要程度: 4

這是帶有表格(table)和表單(form)的文檔。

如何查找?……

  1. 帶有 ?id="age-table"? 的表格。
  2. 表格內(nèi)的所有 ?label? 元素(應(yīng)該有三個)。
  3. 表格中的第一個 ?td?(帶有 “Age” 字段)。
  4. 帶有 ?name="search"? 的 ?form?。
  5. 表單中的第一個 ?input?。
  6. 表單中的最后一個 ?input?。

在一個單獨的窗口中打開 table.html 頁面,并對此頁面使用瀏覽器開發(fā)者工具。


解決方案

實現(xiàn)的方式有很多種。

以下列舉的是其中一些方法:

// 1. 帶有 id="age-table" 的表格。
let table = document.getElementById('age-table')

// 2. 表格內(nèi)的所有 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" 的 form。
// 假設(shè)文檔中只有一個 name="search" 的元素
let form = document.getElementsByName('search')[0]
// 或者,專門對于 form
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] // 取出最后一個


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號