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 を持つ要素は1つだけ存在できます。

同じ id を持つ要素が複数ある場合、それを使用するメソッドの動作は予測できません。たとえば、document.getElementById はそのような要素のいずれかをランダムに返す可能性があります。そのため、ルールを守り、id を一意にしてください。

document.getElementById のみ、anyElem.getElementById は不可

getElementById メソッドは、document オブジェクトに対してのみ呼び出すことができます。ドキュメント全体で指定された id を検索します。

querySelectorAll

最も汎用性の高いメソッドである elem.querySelectorAll(css) は、指定された CSS セレクターに一致する elem 内のすべての要素を返します。

ここでは、最後のchilsであるすべての <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')` は、ポインターが現在上にある要素のコレクションを返します(ネスト順:最も外側の `` から最もネストされたものまで)。

querySelector

elem.querySelector(css) の呼び出しは、指定された CSS セレクターの最初の要素を返します。

言い換えれば、結果は `elem.querySelectorAll(css)[0]` と同じですが、後者は*すべて*の要素を探して1つを選んでいますが、`elem.querySelector` は1つだけを探します。そのため、高速で、書くのも短くなります。

matches

以前のメソッドはDOMを検索していました。

elem.matches(css) は何も探しませんが、単に `elem` が指定された CSS セレクターに一致するかどうかをチェックします。`true` または `false` を返します。

このメソッドは、要素を反復処理し(配列など)、関心のある要素をフィルタリングする場合に便利です。

例えば

<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` の文字を忘れることがあります。つまり、`getElementsByTagName` の代わりに `getElementByTagName` を呼び出そうとします.

`getElementById` には `s` の文字がありません。これは、単一の要素を返すためです。しかし、`getElementsByTagName` は要素のコレクションを返すため、内部に `s` があります.

要素ではなく、コレクションを返します!

もう1つの広く見られる初心者の間違いは、次のように書くことです.

// 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*` は、*ライブ* コレクションを返します。このようなコレクションは、常にドキュメントの現在の状態を反映し、変更があると「自動更新」されます。

以下の例では、2つのスクリプトがあります。

  1. 最初のスクリプトは、`<div>` のコレクションへの参照を作成します。現在のところ、その長さは `1` です。
  2. 2番目のスクリプトは、ブラウザがもう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` は*静的* コレクションを返します。要素の固定配列のようなものです.

代わりにそれを使用すると、両方のスクリプトは `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 でノードを検索するための主なメソッドは 6 つあります。

メソッド 検索対象... 要素で呼び出すことができますか? ライブ?
querySelector CSS セレクター -
querySelectorAll CSS セレクター -
getElementById id - -
getElementsByName name -
getElementsByTagName タグまたは `'*'`
getElementsByClassName クラス

最もよく使用されるのは `querySelector` と `querySelectorAll` ですが、`getElement(s)By*` は散発的に役立つ場合や、古いスクリプトで見つかる場合があります.

そのほかに

  • `elem` が指定された CSS セレクターに一致するかどうかを確認するための `elem.matches(css)` があります。
  • 指定された CSS セレクターに一致する最も近い祖先を探すための `elem.closest(css)` があります。`elem` 自体もチェックされます.

また、子と親の関係を確認するもう1つのメソッドをここで紹介します。これは時々役立つためです.

  • `elemA.contains(elemB)` は、`elemB` が `elemA` 内にある(`elemA` の子孫である)場合、または `elemA==elemB` の場合に true を返します。

タスク

重要度: 4

テーブルとフォームを含むドキュメントを以下に示します。

検索方法…

  1. `id="age-table"` のテーブル。
  2. そのテーブル内のすべての `label` 要素(3つあるはずです)。
  3. そのテーブルの最初の `td`(「年齢」という単語を含む)。
  4. `name="search"` の `form`。
  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 の issue またはプルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • 数語のコードを挿入するには、`<code>` タグを使用し、複数行の場合は `<pre>` タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。