こんにちは、かつコーチです。
前回はDOMの仕組みについて解説し、DOM操作は「要素を取得する」→「要素を操作する」の2ステップが基本だとお伝えしました。
今回はその1ステップ目、「目的の要素をどうやって探し出すか」を扱っていきます。
要素の取得方法にはいくつか種類がありますが、その中でも今主流になっている querySelector を中心に見ていきましょう。
要素を取得する方法は複数ある
昔からある取得方法
JavaScriptには、実は querySelector が登場する前から使われている取得方法があります。
代表的なのが getElementById と getElementsByClassName です。
<div id="header">ヘッダーです</div>
<p class="text">本文です</p>
const header = document.getElementById("header");
console.log(header.textContent);
// "ヘッダーです"
const texts = document.getElementsByClassName("text");
console.log(texts[0].textContent);
// "本文です"
getElementById はid属性の値、getElementsByClassName はclass属性の値を指定して要素を取得します。
古くからある分、対応しているブラウザが非常に多いというメリットがあります。
CSSセレクタで取得できるquerySelector
一方 querySelector は、CSSのセレクタと同じ書き方で要素を取得できる、比較的新しい方法です。
const header = document.querySelector("#header");
console.log(header.textContent);
// "ヘッダーです"
const text = document.querySelector(".text");
console.log(text.textContent);
// "本文です"
id属性を指定するときは # を、class属性を指定するときは . を、CSSと同じルールでつけるだけです。
タグ名だけを指定したり、親子関係を指定したりすることもできます。
const firstParagraph = document.querySelector("p");
// ページ内で最初に見つかった<p>要素
const nested = document.querySelector("div.card > p.title");
// class="card"のdivの直下にある、class="title"のp要素
CSSを書いたことがある人であれば、この書き方の方がとっつきやすいはずです。
querySelectorとquerySelectorAllの違い
1つだけ取得するか、複数取得するか
querySelector は、条件に合う要素のうち最初の1つだけを返します。
ページの中に同じclassを持つ要素が複数あっても、querySelector では最初の1つしか取得できません。
複数の要素をまとめて取得したい場合は、querySelectorAll を使います。
<ul>
<li class="item">りんご</li>
<li class="item">みかん</li>
<li class="item">ぶどう</li>
</ul>
const first = document.querySelector(".item");
console.log(first.textContent);
// "りんご"(最初の1つだけ)
const all = document.querySelectorAll(".item");
console.log(all.length);
// 3(全部で3件)
all.forEach(function (item) {
console.log(item.textContent);
});
// りんご
// みかん
// ぶどう
querySelectorAll は、forEach で1件ずつ処理できる NodeList という形式で結果を返してくれるのが特徴です。
つまずきポイント:NodeListは配列そのものではない
ここで私が実際につまずいた経験があります。
querySelectorAll の戻り値であるNodeListに対して、配列専用のメソッドを使おうとしてエラーになったことがありました。
❌ Before:NodeListに配列のmapメソッドを使おうとしてエラーになる
const items = document.querySelectorAll(".item");
const texts = items.map(function (item) {
return item.textContent;
});
// Uncaught TypeError: items.map is not a function
NodeListは配列に「似ている」データ構造ですが、配列そのものではありません。
そのため、map や filter といった配列専用のメソッドをそのまま呼び出すとエラーになってしまいます。
✅ After:Array.fromで配列に変換してからmapを使う
const items = document.querySelectorAll(".item");
const texts = Array.from(items).map(function (item) {
return item.textContent;
});
console.log(texts);
// ["りんご", "みかん", "ぶどう"]
Array.from() を使ってNodeListを配列に変換すれば、map や filter といった配列のメソッドを問題なく使えるようになります。
このエラーで30分近く原因を探した記憶があり、それ以来「NodeListは配列じゃない」と自分に言い聞かせるようにしています。
querySelectorとgetElementByIdなどの使い分け
どちらを使えばいいのか
「結局どちらを使えばいいの?」と迷う人も多いので、判断軸を整理しておきます。
| 取得方法 | 書き方 | 取得件数 |
|---|---|---|
getElementById | id属性名のみ | 1件 |
getElementsByClassName | class属性名のみ | 複数(HTMLCollection) |
querySelector | CSSセレクタ全般 | 1件(最初のみ) |
querySelectorAll | CSSセレクタ全般 | 複数(NodeList) |
これから新しくコードを書くのであれば、基本的に querySelector と querySelectorAll を使えば十分です。
CSSセレクタの書き方さえ覚えておけば、id・class・タグ名・親子関係など、あらゆる条件を同じ書き方で指定できるためです。
ただし、既存のコードを読むときには getElementById もよく登場するので、両方の書き方を知っておくと安心です。
HTMLCollectionとNodeListの違いにも注意
余談ですが、getElementsByClassName が返すHTMLCollectionは、NodeListともまた少し性質が異なります。
HTMLCollectionは「ライブ(live)」なコレクションで、DOMの変化にあわせて中身が自動的に更新されます。
一方 querySelectorAll が返すNodeListは「静的(static)」で、取得した時点のスナップショットのままです。
初心者のうちは深く気にしなくても大丈夫ですが、「後から要素を追加・削除するコードで挙動が変わることがある」ということだけ頭の片隅に置いておいてください。
まとめ
この記事のポイント
querySelectorはCSSセレクタと同じ書き方で要素を取得できるquerySelectorは最初の1件のみ、querySelectorAllは複数件をNodeListで取得する- NodeListは配列に似ているが配列ではないため、
mapなどを使うにはArray.from()で変換する - 迷ったら
querySelector/querySelectorAllを基本の選択肢にする getElementsByClassNameが返すHTMLCollectionは、DOMの変化に追従する「ライブ」なコレクションである
次に読むべき記事
要素を取得できるようになったところで、次回はその要素を「追加」「削除」する方法を解説します。
→ 次の記事:要素の追加・削除(createElement, appendChild等)