JavaScript querySelector
とは?使い方を徹底解説
Web開発において、JavaScriptを使ってHTML要素を操作することは日常茶飯事です。要素のコンテンツを変更したり、スタイルを適用したり、イベントリスナーを追加したりと、様々なインタラクションを実現するためには、まず操作対象となるHTML要素を正確に特定し、取得する必要があります。
かつて、JavaScriptで要素を取得する主な方法は限られていました。例えば、特定のIDを持つ要素を取得するにはgetElementById
、特定のクラスを持つ要素のリストを取得するにはgetElementsByClassName
、特定のタグ名を持つ要素のリストを取得するにはgetElementsByTagName
などです。これらのメソッドはシンプルで分かりやすい反面、要素を特定するための条件が限られていました。
例えば、「特定のクラスを持つ要素のうち、さらに特定の属性を持つ最初の要素」や、「ある要素の直後にある特定のタグ名の兄弟要素」といった、より複雑な条件で要素を取得したい場合、これらの従来のメソッドだけでは難しく、取得した要素リストに対してJavaScriptでさらにフィルタリングを行うなどの手間が必要でした。
このような背景から登場したのが、querySelector
とquerySelectorAll
というメソッドです。これらのメソッドは、CSSセレクタという強力な仕組みを利用して要素を取得できるため、非常に柔軟かつ効率的に目的の要素にアクセスすることが可能になりました。
この記事では、その中でも特に単一の要素を取得する際に頻繁に利用されるquerySelector
メソッドに焦点を当て、その基本的な使い方から、様々なCSSセレクタの活用、応用、そして使用する上での注意点までを徹底的に解説します。この記事を読み終える頃には、JavaScriptによるDOM要素の取得において、querySelector
を自信を持って使いこなせるようになっているでしょう。
1. querySelector
の基本を知る
まず、querySelector
メソッドの基本的な役割と構文、戻り値について理解しましょう。
1.1. querySelector
の定義と役割
querySelector
メソッドは、Documentインターフェース(またはElementインターフェース)が提供するメソッドです。このメソッドは、引数として渡されたCSSセレクタ文字列に一致する要素を、ドキュメント(または特定の要素)内で検索し、最初に見つかった要素を返します。
「最初に見つかった要素」という点が重要です。指定したセレクタに一致する要素が複数存在する場合でも、querySelector
は常に最初の1つだけを返します。複数の要素を取得したい場合は、後述するquerySelectorAll
を使用する必要があります。
1.2. querySelector
の構文
基本的な構文は以下の通りです。
javascript
let element = document.querySelector(selector);
または、特定の要素の子孫要素の中から検索する場合:
javascript
let targetElement = parentElement.querySelector(selector);
document
: 通常はドキュメント全体から要素を検索する場合に使用します。parentElement
: 特定の要素(例:div
,section
など)の子孫要素の中から検索を開始したい場合に使用します。selector
: 取得したい要素を指定するためのCSSセレクタ文字列です。このセレクタの書き方が、querySelector
の柔軟性の核となります。element
/targetElement
: セレクタに一致した最初の要素オブジェクトが格納されます。
1.3. querySelector
の戻り値
querySelector
メソッドの戻り値は以下のいずれかです。
- 指定したセレクタに一致する要素がドキュメント(または指定した要素)内に見つかった場合:その要素を表すElementオブジェクト。
- 指定したセレクタに一致する要素がドキュメント(または指定した要素)内に見つからなかった場合:
null
。
要素が見つからなかった場合にnull
が返されるという点を覚えておくことは重要です。取得した要素を操作する前に、null
でないか確認するコードを書くことが、エラーを防ぐための良い習慣となります。
1.4. 簡単なCSSセレクタを使った使用例
最も一般的で簡単なCSSセレクタを使ってquerySelector
を試してみましょう。
以下のシンプルなHTML構造があるとします。
“`html
Webサイトタイトル
はじめに
これは最初の段落です。
これは重要な段落です。
コンテンツ
記事の最初の段落。
記事の二番目の段落。
別の記事の段落。
“`
このHTMLに対して、JavaScriptでquerySelector
を使ってみます。
例1: IDセレクタで要素を取得する
特定のIDを持つ要素はドキュメント内で一意であることが保証されているため、IDセレクタ(#id名
)は最もシンプルで確実な要素取得方法の一つです。
“`javascript
// IDが “page-header” の要素を取得
const headerElement = document.querySelector(‘#page-header’);
if (headerElement) {
console.log(‘ヘッダー要素が見つかりました:’, headerElement);
// 例: ヘッダーの背景色を変更
headerElement.style.backgroundColor = ‘#f0f0f0’;
} else {
console.log(‘指定されたIDの要素は見つかりませんでした。’);
}
// IDが “non-existent-id” の要素を取得 (存在しないID)
const nonExistentElement = document.querySelector(‘#non-existent-id’);
if (nonExistentElement) {
console.log(‘見つかりました(これは表示されないはず):’, nonExistentElement);
} else {
console.log(‘存在しないIDを指定した場合:’, nonExistentElement); // null が表示される
}
“`
例2: クラスセレクタで要素を取得する
特定のクラスを持つ要素を取得するには、クラスセレクタ(.クラス名
)を使用します。クラスは複数の要素に適用される可能性があるため、querySelector
は一致した要素のうち、DOMツリーで最初に登場する要素を返します。
“`javascript
// クラス名が “highlight” の最初の要素を取得
const firstHighlightElement = document.querySelector(‘.highlight’);
if (firstHighlightElement) {
console.log(‘最初のハイライト要素が見つかりました:’, firstHighlightElement);
// 例: その要素のテキストを変更
firstHighlightElement.textContent = ‘この段落はJavaScriptで変更されました!’;
} else {
console.log(‘指定されたクラスの要素は見つかりませんでした。’);
}
“`
上記のHTMLでは、.intro-section
内のp.highlight
と、.content-section
内のdiv.article.highlight
があります。querySelector('.highlight')
は、これらのうちDOMツリーで先に登場する.intro-section
内の<p class="highlight">
要素を取得します。
例3: タグセレクタで要素を取得する
特定のタグ名を持つ要素を取得するには、タグセレクタ(タグ名
)を使用します。これも複数存在する可能性があるため、最初の要素が取得されます。
“`javascript
// 最初の
要素を取得
const firstParagraph = document.querySelector(‘p’);
if (firstParagraph) {
console.log(‘最初の段落要素が見つかりました:’, firstParagraph);
// 例: その要素に下線を追加
firstParagraph.style.textDecoration = ‘underline’;
} else {
console.log(‘段落要素が見つかりませんでした。’);
}
“`
上記のHTMLでは、<main>
内の最初の<p>
(”これは最初の段落です。”)が取得されます。
このように、querySelector
は非常にシンプルに使うことができます。しかし、その真価は、より複雑なCSSセレクタを組み合わせることで発揮されます。
2. 様々なCSSセレクタを利用する
querySelector
の最大の強みは、Webサイトのデザインやスタイリングに使用するのと同じCSSセレクタ構文を、要素の取得にそのまま利用できる点です。これにより、非常に細かく、目的の要素をピンポイントで指定することが可能になります。
以下に、querySelector
で利用できる様々なCSSセレクタの種類と、それぞれの使用例を解説します。
2.1. 基本的なセレクタ
- 要素セレクタ (Type Selector): 特定のタグ名を持つ要素を選択します。
- 例:
div
,p
,a
,img
- JavaScript:
document.querySelector('div');
(最初のdiv
要素)
- 例:
- IDセレクタ (ID Selector): 特定のIDを持つ要素を選択します。IDはドキュメント内で一意であるべきです。
- 例:
#header
,#sidebar
- JavaScript:
document.querySelector('#header');
- 例:
- クラスセレクタ (Class Selector): 特定のクラス名を持つ要素を選択します。
- 例:
.button
,.active
,.item
- JavaScript:
document.querySelector('.button');
(クラスがbutton
の最初の要素)
- 例:
- ユニバーサルセレクタ (Universal Selector): ドキュメント内のすべての要素を選択します。(通常、
querySelector
ではあまり使いませんが、他のセレクタと組み合わせて使われることがあります。)- 例:
*
- JavaScript:
document.querySelector('*');
(ドキュメント内の最初の要素、通常は<html>
または<!DOCTYPE html>
宣言の次のノード)
- 例:
2.2. 属性セレクタ (Attribute Selectors)
要素が持つ属性に基づいて要素を選択します。特定の属性が存在するか、属性値が特定の値と一致するか、部分的に一致するかなど、様々な条件を指定できます。
- 属性が存在するか (
[attribute]
): 指定した属性が存在するすべての要素。- 例:
[href]
,[disabled]
- JavaScript:
document.querySelector('[href]');
(最初のhref
属性を持つ要素)
- 例:
- 属性値が完全に一致するか (
[attribute="value"]
): 指定した属性が、正確に指定した値を持つすべての要素。- 例:
[type="text"]
,[data-status="pending"]
- JavaScript:
document.querySelector('[type="text"]');
(最初のtype="text"
属性を持つ要素)
- 例:
- 属性値が指定した値で始まるか (
[attribute^="value"]
): 指定した属性の値が、指定した文字列で始まるすべての要素。- 例:
[href^="https://"]
,[id^="item-"]
- JavaScript:
document.querySelector('[href^="https://"]');
(最初のhref
属性値がhttps://
で始まる要素)
- 例:
- 属性値が指定した値で終わるか (
[attribute$="value"]
): 指定した属性の値が、指定した文字列で終わるすべての要素。- 例:
[src$=".png"]
,[alt$="_image"]
- JavaScript:
document.querySelector('[src$=".png"]');
(最初のsrc
属性値が.png
で終わる要素)
- 例:
- 属性値に指定した値が含まれるか (
[attribute*="value"]
): 指定した属性の値に、指定した文字列が部分的に含まれるすべての要素。- 例:
[class*="button"]
(クラス名に”button”を含む),[title*="important"]
- JavaScript:
document.querySelector('[class*="button"]');
(クラス名に”button”を含む最初の要素)
- 例:
- 空白区切りの属性値リストに指定した値が単語として含まれるか (
[attribute~="value"]
): スペースで区切られた属性値のリスト(主にclass
属性)に、指定した単語が単体として含まれるすべての要素。- 例:
[class~="active"]
(クラスリストに”active”クラスが含まれる) - JavaScript:
document.querySelector('[class~="active"]');
(クラスリストに”active”が含まれる最初の要素)
- 例:
- ハイフン区切りの属性値リストが指定した値で始まるか (
[attribute|="value"]
): ハイフンで区切られた属性値のリストが、指定した値で始まるすべての要素。主にlang
属性に使用されます(例:en-US
,en-GB
など)。- 例:
[lang|="en"]
(言語が英語 (en) または英語のバリアント (en-US, en-GBなど) ) - JavaScript:
document.querySelector('[lang|="en"]');
(lang属性が”en”または”en-“で始まる最初の要素)
- 例:
属性セレクタは、クラス名やID名に依存しすぎることなく、要素のより具体的な状態やタイプに基づいて要素を選択したい場合に非常に役立ちます。特に、データ属性(data-*
)と組み合わせて使うことで、アプリケーション固有の状態や情報を要素に持たせ、それを元に要素を選択する強力なパターンを構築できます。
2.3. 結合子 (Combinators)
複数のセレクタを組み合わせて、要素間の関係(親子、兄弟、子孫など)に基づいて要素を選択します。
- 子孫結合子 (Descendant Combinator):
selector1 selector2
–selector1
の子孫であるselector2
の要素を選択します。子孫とは、子、孫、曾孫…など、すべての階層を含みます。- 例:
div p
(すべてのdiv
要素内のすべてのp
要素) - JavaScript:
document.querySelector('div p');
(最初のdiv
要素内の最初のp
要素)
- 例:
- 子結合子 (Child Combinator):
selector1 > selector2
–selector1
の直接の子であるselector2
の要素を選択します。- 例:
ul > li
(すべてのul
要素の直接の子であるすべてのli
要素) - JavaScript:
document.querySelector('ul > li');
(最初のul
要素の最初の直接の子であるli
要素)
- 例:
- 隣接兄弟結合子 (Adjacent Sibling Combinator):
selector1 + selector2
–selector1
の直後にあり、同じ親を持つselector2
の要素を選択します。- 例:
h2 + p
(h2
要素の直後に続く最初のp
要素) - JavaScript:
document.querySelector('h2 + p');
(最初のh2
要素の直後に続く最初のp
要素)
- 例:
- 一般兄弟結合子 (General Sibling Combinator):
selector1 ~ selector2
–selector1
の後にあり、同じ親を持つselector2
のすべての要素を選択します。(querySelector
では最初の一つのみ)- 例:
h2 ~ p
(h2
要素の後に続くすべてのp
要素) - JavaScript:
document.querySelector('h2 ~ p');
(最初のh2
要素の後に続く最初のp
要素)
- 例:
これらの結合子を使うことで、「ヘッダー内のナビゲーションリストの最初のリンク」「特定の記事ブロック内の最初の画像」といった、構造に基づいた要素の取得が可能になります。
2.4. 擬似クラス (Pseudo-classes)
要素の特定の状態や構造的な位置に基づいて要素を選択します。要素が実際に存在するわけではなく、「〜のような状態の要素」「〜番目の子要素」といった概念的な状態を表します。
-
状態擬似クラス:
:hover
: マウスポインターが要素上にある状態。:focus
: 要素がフォーカスを受け取っている状態(フォーム入力要素など)。:active
: ユーザーが要素をアクティブ化している状態(クリックなど)。:visited
: 既に訪問済みのリンク。:link
: 未訪問のリンク。:checked
: チェックボックスやラジオボタンがチェックされている状態、または<option>
が選択されている状態。:disabled
: 無効化された要素。:enabled
: 有効化された要素。:read-only
: 読み取り専用の入力要素。:read-write
: 編集可能な入力要素。- JavaScriptでの例:
document.querySelector('input:checked');
(チェックされている最初のinput
要素)
-
構造的擬似クラス:
:first-child
: 親要素の最初の子要素である要素。:last-child
: 親要素の最後の子要素である要素。:nth-child(n)
: 親要素から数えてn番目の子要素である要素。n
には数字、odd
(奇数),even
(偶数) または式 (an+b
) を指定可能。:nth-last-child(n)
: 親要素の最後から数えてn番目の子要素である要素。:only-child
: 親要素にとって唯一の子要素である要素。:first-of-type
: 親要素の子要素のうち、同じタグ名を持つ兄弟要素の中で最初のもの。:last-of-type
: 親要素の子要素のうち、同じタグ名を持つ兄弟要素の中で最後のもの。:nth-of-type(n)
: 親要素の子要素のうち、同じタグ名を持つ兄弟要素の中でn番目のもの。:nth-last-of-type(n)
: 親要素の子要素のうち、同じタグ名を持つ兄弟要素の中で最後からn番目のもの。:only-of-type
: 親要素にとって、同じタグ名を持つ唯一の子要素である要素。- JavaScriptでの例:
document.querySelector('li:first-child');
(最初のli
要素)document.querySelector('p:nth-child(2)');
(親要素の2番目の子要素である最初のp
要素)document.querySelector('.item:last-of-type');
(クラスがitem
の要素のうち、兄弟要素の中で最後に登場するitem
クラスの要素)
-
その他の擬似クラス:
:not(selector)
: 指定したセレクタに一致しない要素を選択します。- 例:
div:not(.container)
(container
クラスを持たないすべてのdiv
) - JavaScript:
document.querySelector('div:not(.container)');
(container
クラスを持たない最初のdiv
)
- 例:
:root
: ドキュメントのルート要素(通常は<html>
)。- JavaScript:
document.querySelector(':root');
(これはdocument.documentElement
と同じ要素を取得します)
- JavaScript:
:empty
: 子要素(テキストノードも含む)を全く持たない要素。- JavaScript:
document.querySelector('div:empty');
(最初の子要素を持たないdiv
)
- JavaScript:
擬似クラスは、DOM構造を走査するだけでは難しい条件での要素取得を可能にします。例えば、フォームのバリデーションエラーがある要素を取得したり、リストの項目を一つおきに選択したりといった場合に便利です。
2.5. 擬似要素 (Pseudo-elements)
要素の特定の部分を選択したり、存在しない要素(コンテンツの前や後)を表現したりします。
::before
: 要素のコンテンツの直前に生成される擬似要素。::after
: 要素のコンテンツの直後に生成される擬似要素。::first-line
: ブロック要素の最初の行。::first-letter
: ブロック要素の最初の文字。::selection
: ユーザーが選択(ハイライト)した部分。
重要: querySelector
メソッドは、HTML DOM要素を返します。擬似要素(::before
, ::after
など)はCSSによって生成されるものであり、実際のDOMツリーには存在しないため、querySelector
を使って擬似要素自体を取得することはできません。querySelector
を使って取得できるのは、それらの擬似要素が適用されている実際のDOM要素のみです。
例えば、document.querySelector('p::before')
を実行しても、CSSで生成された::before
コンテンツを取得することはできません。これは重要な点なので注意してください。
3. querySelector
の応用的な使い方
querySelector
は単にdocument
に対して使うだけでなく、特定の要素を起点として検索範囲を限定したり、動的にセレクタを生成したりといった応用的な使い方が可能です。
3.1. 特定の要素内での検索
前述の構文でも触れましたが、querySelector
はdocument
だけでなく、任意のElementオブジェクトに対しても呼び出すことができます。
“`javascript
let container = document.querySelector(‘#main-content’); // まずコンテナ要素を取得
if (container) {
// コンテナ要素の子孫の中から、最初の
要素を取得
let firstParagraphInContainer = container.querySelector(‘p’);
if (firstParagraphInContainer) {
console.log('コンテナ内の最初の段落:', firstParagraphInContainer);
firstParagraphInContainer.style.color = 'blue';
}
// コンテナ要素の子孫の中から、クラスが "action-button" の最初の要素を取得
let actionButton = container.querySelector('.action-button');
if (actionButton) {
console.log('コンテナ内のアクションボタン:', actionButton);
actionButton.textContent = 'クリックしてください!';
}
} else {
console.log(‘#main-content 要素が見つかりませんでした。’);
}
“`
この「特定の要素内での検索」は非常に有用です。
- スコープの限定: 検索範囲を狭めることで、意図しない要素を取得するリスクを減らせます。特に大規模なDOMや、複数のコンポーネントが混在するページで有効です。
- パフォーマンス: 理論的には、検索対象となるDOMツリーが小さくなるため、わずかにパフォーマンスが向上する可能性があります(ただし、ほとんどの場合、体感できるほどの差はありません)。
- コードの可読性: どの範囲で要素を探しているかが明確になり、コードが読みやすくなります。
例えば、ブログ記事を表示するコンポーネント内で、その記事内の見出しや画像を取得したい場合、ドキュメント全体ではなく、その記事要素自体に対してquerySelector
を使用するのが自然なアプローチです。
3.2. 動的なセレクタの生成
JavaScriptの変数やテンプレートリテラルを使って、実行時にセレクタ文字列を動的に生成することも可能です。これは、ユーザーの入力やアプリケーションの状態に基づいて要素を選択したい場合に役立ちます。
``javascript
[data-item-id=”${itemId}”]`;
function updateItemStatus(itemId, status) {
// 例: 'data-item-id' 属性を使って特定のアイテム要素を選択
const itemSelector =
const itemElement = document.querySelector(itemSelector);
if (itemElement) {
// 例: アイテム要素内のステータス表示要素を選択
const statusElement = itemElement.querySelector('.status');
if (statusElement) {
statusElement.textContent = status;
// 既存のステータスクラスを削除し、新しいクラスを追加
itemElement.classList.remove('status-pending', 'status-completed', 'status-cancelled');
itemElement.classList.add(`status-${status.toLowerCase()}`);
}
} else {
console.error(`アイテムID ${itemId} の要素が見つかりませんでした。`);
}
}
// 使用例
// HTML:
updateItemStatus(‘123’, ‘Completed’);
“`
この例では、テンプレートリテラルを使って、引数として渡されたitemId
に基づいた属性セレクタを生成しています。これにより、特定のデータ属性を持つ要素を柔軟に選択できます。
3.3. 取得した要素の操作
querySelector
で取得した要素は、通常のDOM要素オブジェクトです。したがって、その要素に対して様々なDOM操作を行うことができます。
“`javascript
const myElement = document.querySelector(‘#my-element’);
if (myElement) {
// テキストコンテンツの変更
myElement.textContent = ‘新しいテキスト’;
// HTMLコンテンツの変更
myElement.innerHTML = '<strong>強調されたテキスト</strong>';
// クラスの追加・削除
myElement.classList.add('active');
myElement.classList.remove('hidden');
if (myElement.classList.contains('active')) {
console.log('要素は現在アクティブです。');
}
myElement.classList.toggle('highlight'); // クラスがあれば削除、なければ追加
// スタイルの変更
myElement.style.color = 'red';
myElement.style.fontSize = '16px';
// 属性の変更
myElement.setAttribute('data-status', 'processed');
const status = myElement.getAttribute('data-status');
console.log('データ属性の値:', status);
if (myElement.hasAttribute('data-status')) {
myElement.removeAttribute('data-status');
}
// イベントリスナーの追加
myElement.addEventListener('click', function() {
alert('要素がクリックされました!');
});
// 要素の追加 (子要素として)
const newParagraph = document.createElement('p');
newParagraph.textContent = '新しく追加された段落。';
myElement.appendChild(newParagraph);
// 要素の削除 (自分自身を削除)
// myElement.remove(); // 新しい方法
// myElement.parentNode.removeChild(myElement); // 古い方法
} else {
console.log(‘指定された要素が見つかりませんでした。操作できません。’);
}
“`
要素の取得とそれに続く操作は、JavaScriptによるDOM操作の基本サイクルです。querySelector
はその最初のステップを非常に効率的にしてくれます。
4. querySelectorAll
との比較と使い分け
querySelector
とよく似たメソッドにquerySelectorAll
があります。両者は同じCSSセレクタ構文を使用しますが、目的と戻り値が異なります。
4.1. querySelectorAll
の基本
querySelectorAll
メソッドは、DocumentインターフェースまたはElementインターフェースで提供され、引数として渡されたCSSセレクタ文字列に一致するすべての要素を、ドキュメント(または特定の要素)内で検索し、それらの要素をまとめたリストを返します。
構文はquerySelector
と似ています。
javascript
let elements = document.querySelectorAll(selector);
または、特定の要素の子孫要素の中から検索する場合:
javascript
let targetElements = parentElement.querySelectorAll(selector);
document
/parentElement
: 検索を開始する対象。selector
: 取得したい要素を指定するためのCSSセレクタ文字列。elements
/targetElements
: セレクタに一致したすべての要素を含むNodeListが格納されます。
4.2. querySelectorAll
の戻り値:NodeList
querySelectorAll
は、一致する要素をNodeListというオブジェクトとして返します。
NodeListは、要素の集合(コレクション)を表すオブジェクトです。配列のようにインデックス(0から始まる番号)を使って各要素にアクセスしたり、length
プロパティで要素の数を取得したりできます。
“`javascript
const listItems = document.querySelectorAll(‘#my-list li’); // #my-list 内のすべての
console.log(‘取得した要素の数:’, listItems.length); // NodeList の要素数
// インデックスで個別の要素にアクセス
if (listItems.length > 0) {
console.log(‘最初の要素:’, listItems[0]);
console.log(‘二番目の要素:’, listItems[1]);
}
“`
NodeListの重要な特徴として、「静的(Static)」であることが挙げられます。querySelectorAll
が実行された時点のDOMの状態を反映しており、その後DOMに要素が追加・削除されても、既に取得したNodeListの内容は変化しません。これは、getElementsByClassName
やgetElementsByTagName
が返す「ライブ(Live)」なHTMLCollectionとは異なる点です。(HTMLCollectionは、DOMの変更に合わせて内容が自動的に更新されます。)
NodeListに含まれる各要素に対して処理を行いたい場合は、ループ処理が必要です。NodeListは配列ではありませんが、多くの配列メソッド(例: forEach
, map
, filter
など)を使用できます。または、従来のfor
ループやfor...of
ループも使用可能です。
“`javascript
const allParagraphs = document.querySelectorAll(‘p’); // すべての
要素を取得
// forEach を使ったループ処理 (よく使われる)
allParagraphs.forEach(paragraph => {
paragraph.style.border = ‘1px solid #ccc’; // 各段落に枠線を追加
});
// for…of を使ったループ処理
for (const paragraph of allParagraphs) {
paragraph.classList.add(‘processed’); // 各段落にクラスを追加
}
// 従来の for ループ
for (let i = 0; i < allParagraphs.length; i++) {
allParagraphs[i].textContent += ‘ [処理済み]’; // 各段落のテキストを変更
}
“`
4.3. querySelector
とquerySelectorAll
の使い分け
どちらのメソッドを使うべきかは、取得したい要素の数と、その後の処理によって決まります。
-
querySelector
を使うべき場合:- ドキュメント全体、または特定の範囲で、特定の条件に一致する要素が1つだけ存在することが分かっている場合(例: IDを持つ要素、ページタイトルを表す
h1
、メインナビゲーションなど)。 - 特定の条件に一致する要素が複数存在する可能性があるが、最初に一致した要素だけが必要な場合。
- ドキュメント全体、または特定の範囲で、特定の条件に一致する要素が1つだけ存在することが分かっている場合(例: IDを持つ要素、ページタイトルを表す
-
querySelectorAll
を使うべき場合:- 特定の条件に一致する要素が複数存在し、それらすべてに対して同じ処理を行いたい場合(例: リストの各項目、テーブルの各行、特定のクラスを持つすべてのボタンなど)。
- 一致する要素が1つだけ、あるいは存在しない可能性があるが、NodeListとして結果を受け取りたい場合(後から要素数をチェックしたり、ループ処理で対応したりするため)。
例えば、「ページ内の主要なフォーム」を取得したい場合はquerySelector('#main-form')
、「すべての入力フィールド」を取得したい場合はquerySelectorAll('input, select, textarea')
のように使い分けます。
4.4. パフォーマンスに関する考慮事項
一般的に、querySelector
やquerySelectorAll
は、古いgetElementById
などと比較してわずかにパフォーマンスが劣ると言われることがあります。これは、CSSセレクタの解析と、一致する要素をDOMツリー内で検索するための処理が必要になるためです。
しかし、現代のブラウザは非常に高速化されており、ほとんどの一般的なウェブページやアプリケーションにおいて、querySelector
やquerySelectorAll
のパフォーマンス差が問題になることは稀です。極めて複雑なセレクタを使用する場合や、非常に大きなDOMツリーに対してこれらのメソッドを頻繁かつ繰り返し実行する場合などでなければ、パフォーマンスを過度に心配する必要はありません。
むしろ、CSSセレクタの柔軟性によって、JavaScript側での複雑な要素フィルタリングロジックが不要になり、コードが簡潔で分かりやすくなるメリットの方が大きい場合が多いです。
パフォーマンスがクリティカルなシナリオでは、以下の点を考慮すると良いでしょう。
- 可能な限り、シンプルなセレクタを使用する(特にIDセレクタは高速です)。
- 検索範囲を狭める(特定の要素内で
querySelector
やquerySelectorAll
を使用する)。 - 繰り返し同じ要素を取得する必要がある場合は、一度取得した要素を変数にキャッシュしておく。
querySelectorAll
で取得したNodeListを操作する場合、ライブなHTMLCollectionを返すgetElementsBy*
系メソッドの方が適しているケースもありますが、前述の通りNodeListは静的なので意図しない挙動を防げる利点もあります。どちらが良いかは状況によりますが、多くの場合NodeListで十分便利です。
5. querySelector
を使用する際の注意点とベストプラクティス
querySelector
は非常に便利なメソッドですが、適切に使用するためにはいくつかの注意点があります。
5.1. セレクタの正確性
- 無効なセレクタ: 無効なCSSセレクタ文字列を
querySelector
に渡すと、通常はSyntaxError
がスローされます。開発者コンソールでエラーを確認し、セレクタが正しい構文になっているか確認しましょう。 - 存在しない要素: 指定したセレクタに一致する要素がドキュメント内に一つも存在しない場合、
querySelector
はnull
を返します。取得した要素を操作する前に、必ずnull
チェックを行うようにしましょう。
“`javascript
const maybeElement = document.querySelector(‘.non-existent-class’);
if (maybeElement) {
// 要素が存在する場合のみ操作
maybeElement.textContent = ‘見つかった!’;
} else {
// 要素が見つからなかった場合の処理
console.warn(‘指定されたセレクタの要素が見つかりませんでした。’);
}
“`
5.2. 「最初の要素」であることの理解
querySelector
はセレクタに一致する最初の要素のみを返します。複数の要素にスタイルを適用したい、イベントリスナーを登録したいといった場合は、必ずquerySelectorAll
を使用する必要があります。
「最初の要素」の定義は、DOMツリーにおける登場順です。
5.3. パフォーマンスに関する再確認
前述の通り、ほとんどの場合はパフォーマンスを心配する必要はありませんが、パフォーマンスが要求される場面ではセレクタの単純化やキャッシュを検討しましょう。
5.4. エラーハンドリングとnull
チェック
繰り返しになりますが、querySelector
は要素が見つからなかった場合にnull
を返します。取得した変数に対して要素のプロパティにアクセスしたり、メソッドを呼び出したりすると、TypeError
が発生する可能性があります(例: null.textContent
)。これを防ぐために、if (element) { ... }
やオプショナルチェイニング(element?.textContent
– ただし、これはプロパティ/メソッドアクセスのみで、要素自体の存在チェックとしてはif
の方が明確)などを使って、要素が存在することを確認してから操作を行いましょう。
5.5. アクセシビリティとセマンティックなHTML
querySelector
で要素を正確かつ頑健に取得するためには、基となるHTML構造がセマンティックで適切であることが望ましいです。
- ID: ページ内で一意であるべき重要な要素にはIDを付けましょう。
- クラス: 役割や状態を示すためのクラスを使いましょう。スタイリングのためだけでなく、JavaScriptからのセレクタとしても利用価値が高いです。
- データ属性: JavaScriptが特定の要素にアクセスしたり、状態を管理したりするためのフックとして、データ属性(
data-*
)は非常に有用です。プレゼンテーション層(スタイル)とスクリプト層(挙動)を分離しやすくなります。 - セマンティックなタグ:
nav
,article
,aside
,section
,main
,footer
などのHTML5セマンティックタグや、適切な見出しレベル(h1
~h6
)を使用することで、DOM構造が分かりやすくなり、セレクタも書きやすくなります。
適切なHTML構造は、JavaScriptからの要素取得を容易にするだけでなく、SEOやアクセシビリティの向上にも繋がります。
5.6. シャドウDOM内の要素取得
Web Componentsなどで使用されるシャドウDOM(Shadow DOM)は、通常のDOMとは隔離されたツリー構造を持ちます。document.querySelector
はデフォルトではシャドウDOM内の要素を検索しません。
シャドウDOM内の要素を取得したい場合は、そのシャドウルート(Shadow Root)を起点にquerySelector
を呼び出す必要があります。
“`javascript
// あるカスタム要素があると仮定
const customElement = document.querySelector(‘my-custom-element’);
if (customElement && customElement.shadowRoot) {
// シャドウルート内から要素を取得
const shadowButton = customElement.shadowRoot.querySelector(‘#shadow-button’);
if (shadowButton) {
console.log('シャドウDOM内のボタンが見つかりました:', shadowButton);
shadowButton.addEventListener('click', () => {
alert('シャドウボタンがクリックされました!');
});
}
}
“`
シャドウDOMを扱う場合は、検索の起点がどこになるのかを意識する必要があります。
6. 具体的なシナリオ別サンプルコード集
いくつかの具体的なシナリオでquerySelector
(またはquerySelectorAll
との組み合わせ)を使用する例を見てみましょう。
シナリオ1: 特定のフォーム要素の値を取得する
フォーム送信時などに、特定の入力フィールドや選択ボックスの値を取得したい場面はよくあります。
“`html
“`
“`javascript
const userForm = document.querySelector(‘#user-form’);
if (userForm) {
// フォーム内の特定のIDを持つ入力要素を取得
const usernameInput = userForm.querySelector(‘#username’);
const emailInput = userForm.querySelector(‘#email’);
const roleSelect = userForm.querySelector(‘#role’);
// 取得した要素から値を取得
if (usernameInput && emailInput && roleSelect) {
const username = usernameInput.value;
const email = emailInput.value;
const role = roleSelect.value;
console.log('ユーザー名:', username);
console.log('メールアドレス:', email);
console.log('役割:', role);
// フォーム送信イベントを捕捉して処理することも可能
userForm.addEventListener('submit', function(event) {
event.preventDefault(); // デフォルトの送信をキャンセル
const submittedUsername = userForm.querySelector('#username').value;
const submittedEmail = userForm.querySelector('#email').value;
const submittedRole = userForm.querySelector('#role').value;
console.log('--- フォーム送信時 ---');
console.log('ユーザー名:', submittedUsername);
console.log('メールアドレス:', submittedEmail);
console.log('役割:', submittedRole);
console.log('--------------------');
// ここでAjaxリクエストなどでサーバーにデータを送信
// alert('フォームが送信されました(実際には送信していません)。');
});
} else {
console.error('フォーム内の必須要素が見つかりませんでした。');
}
} else {
console.error(‘ユーザーフォーム要素が見つかりませんでした。’);
}
``
querySelector
ここでは、まずフォーム要素全体をで取得し、そのフォーム要素内から各入力要素を
querySelector`で取得しています。これにより、ページ内に他のフォーム要素があっても、意図したフォーム内の要素だけを確実に取得できます。
シナリオ2: データ属性を持つ要素を操作する
データ属性は、JavaScriptから要素にアクセスするためのカスタム情報を埋め込むのに非常に便利です。
“`html
- アイテムA
- アイテムB
- アイテムC
“`
“`javascript
// データ属性 ‘data-item-id’ が ‘102’ の要素を取得
const selectedItem = document.querySelector(‘[data-item-id=”102″]’);
if (selectedItem) {
console.log(‘選択されたアイテム:’, selectedItem.textContent);
// 例: その要素の背景色を変更
selectedItem.style.backgroundColor = ‘yellow’;
}
// さらに、その要素が ‘selected’ クラスも持っているか確認するセレクタ
const explicitlySelectedItem = document.querySelector(‘[data-item-id=”102″].selected’);
if (explicitlySelectedItem) {
console.log(‘明示的に選択されたアイテム:’, explicitlySelectedItem.textContent);
// クラスを持っていることの確認はセレクタ自体が行ってくれる
}
“`
データ属性セレクタを使うことで、IDやクラス名だけに依存せず、要素が持つデータに基づいて柔軟に要素を取得できます。
シナリオ3: ナビゲーション内のアクティブなリンクを取得する
現在のページを示すために、ナビゲーション内の特定のリンクにactive
などのクラスが付与されている場合があります。
“`html
“`
“`javascript
// nav 要素内の、クラスが ‘active’ の最初の 要素を取得
const activeLink = document.querySelector(‘nav a.active’);
if (activeLink) {
console.log(‘アクティブなリンクのテキスト:’, activeLink.textContent);
// 例: アクティブなリンクに特別なスタイルを適用
activeLink.style.fontWeight = ‘bold’;
activeLink.style.color = ‘blue’;
}
“`
結合子(ここではスペース結合子 – 子孫)とクラスセレクタを組み合わせることで、特定のコンテナ内にある、特定の状態(クラス)を持つ要素を正確に取得できます。
シナリオ4: 擬似クラスを利用した要素の取得
構造的擬似クラスなどを使って、位置や状態に基づいて要素を取得します。
“`html
“`
“`javascript
// .message-list 内の最初の
要素を取得 (:first-child は親の最初の子ならどの要素でもマッチ)
const firstMessage = document.querySelector(‘.message-list p:first-child’);
if (firstMessage) {
console.log(‘最初のメッセージ:’, firstMessage.textContent);
}
// .message-list 内の、奇数番目 (1番目、3番目…) の
要素を取得 (querySelectorAll が適切だが、querySelector なら最初の奇数番目)
const firstOddParagraph = document.querySelector(‘.message-list p:nth-of-type(odd)’);
if (firstOddParagraph) {
console.log(‘最初の奇数番目の段落:’, firstOddParagraph.textContent); // メッセージ 1
}
// .message-list 内の、クラスが ‘read’ でない最初の
要素を取得
const firstUnreadMessage = document.querySelector(‘.message-list p:not(.read)’);
if (firstUnreadMessage) {
console.log(‘最初の未読メッセージ:’, firstUnreadMessage.textContent); // メッセージ 1
}
``
nth-child
擬似クラスを使うことで、「リストの最初」「特定の条件を満たさないもの」といった、通常のセレクタだけでは難しい条件で要素を指定できます。や
nth-of-typeの引数には、数式や
odd/
even`を使えるため、非常に柔軟な要素選択が可能です。
7. 従来の取得方法との比較(詳細)
querySelector
やquerySelectorAll
が登場する以前から存在する要素取得メソッドとの違いをより詳しく見てみましょう。
-
getElementById(id)
:- 特徴: 特定のIDを持つ要素を高速に取得します。IDはドキュメント内で一意であるべきなので、戻り値は単一の要素オブジェクトか
null
です。 - メリット: セレクタの解析が不要なため、最も高速な要素取得方法の一つです。シンプルで分かりやすいです。
- デメリット: IDによる取得しかできません。複数の条件を組み合わせたり、親子・兄弟関係を指定したりすることはできません。
querySelector
との比較: 機能は限定的ですが、IDによる単一要素取得に限ればgetElementById('#my-id')
とdocument.querySelector('#my-id')
はほぼ同じ結果を返し、getElementById
の方が理論的には高速です。ただし、パフォーマンス差は通常無視できるレベルです。モダンなコードでは、ID取得も含めquerySelector
で統一することも多いです。
- 特徴: 特定のIDを持つ要素を高速に取得します。IDはドキュメント内で一意であるべきなので、戻り値は単一の要素オブジェクトか
-
getElementsByClassName(className)
:- 特徴: 特定のクラス名を持つすべての要素を、ライブなHTMLCollectionとして返します。
- メリット: 特定のクラスを持つ要素リストを効率的に取得できます。ライブコレクションなので、DOMの変更に合わせて自動的に内容が更新されます。
- デメリット: クラス名による取得しかできません。複数のクラス指定や、他のセレクタ(タグ名、属性など)との組み合わせはできません(例: クラスAとクラスBの両方を持つ要素、のような指定はできません)。
querySelector('.classA.classB')
を使います。 querySelectorAll
との比較:getElementsByClassName('my-class')
は特定のクラスを持つ要素のライブなHTMLCollectionを返します。document.querySelectorAll('.my-class')
は同じ要素の静的なNodeListを返します。リストの要素がDOM操作によって頻繁に増減する場合に、ライブなHTMLCollectionが有利なケースがありますが、NodeListの静的な性質が予期せぬ挙動を防ぎ、forEach
などの配列ライクなメソッドが使えるため、多くの場合querySelectorAll
の方が便利です。
-
getElementsByTagName(tagName)
:- 特徴: 特定のタグ名を持つすべての要素を、ライブなHTMLCollectionとして返します。
- メリット: 特定のタグ名の要素リストを効率的に取得できます。ライブコレクションです。
- デメリット: タグ名による取得しかできません。他のセレクタとの組み合わせはできません。
querySelectorAll
との比較:getElementsByTagName('div')
はすべてのdiv
要素のライブなHTMLCollectionを返します。document.querySelectorAll('div')
はすべてのdiv
要素の静的なNodeListを返します。使い分けの考慮点はgetElementsByClassName
と同様です。
-
getElementsByName(name)
:- 特徴: 特定の
name
属性を持つ要素(主にフォーム要素)を、ライブなNodeListとして返します。 - メリット: フォーム要素などを
name
属性でまとめて取得するのに便利です。 - デメリット:
name
属性を持つ要素のみが対象です。 querySelector
との比較:getElementsByName('my-name')
で複数の要素を取得できます。querySelector('[name="my-name"]')
は最初の要素のみを取得します。全ての要素が必要ならgetElementsByName
、最初の要素だけならquerySelector
が使えます。querySelectorAll('[name="my-name"]')
も使えますが、getElementsByName
が特定の用途に特化している分、より高速である可能性があります。
- 特徴: 特定の
使い分けの指針:
- 特定のIDを持つ単一要素を最速で取得したい:
getElementById
- CSSセレクタの柔軟性を最大限に活用して単一要素を取得したい:
querySelector
- 特定のクラスを持つ要素リストを操作したい(静的なリストで十分):
querySelectorAll
(.className
) - 特定のタグ名を持つ要素リストを操作したい(静的なリストで十分):
querySelectorAll
(tagName
) - CSSセレクタの柔軟性を最大限に活用して複数要素を取得したい:
querySelectorAll
- 特定のクラス名またはタグ名の要素リストを、DOM変更に自動追従させたい:
getElementsByClassName
またはgetElementsByTagName
(ただしNodeListの静的な性質が多くのケースでメリットになるため、querySelectorAll
の方がモダンで一般的) - 特定のname属性を持つフォーム要素リストを操作したい:
getElementsByName
またはquerySelectorAll('[name="..."]')
モダンなJavaScript開発においては、querySelector
とquerySelectorAll
がDOM要素取得の主力となることが多く、ほとんどのケースでこれらのメソッドで事足ります。CSSセレクタの表現力を活かせるため、複雑な条件での要素取得がJavaScriptコード内で直感的に記述できるようになります。
8. まとめ
この記事では、JavaScriptでHTML要素を取得する際に中心的な役割を果たすquerySelector
メソッドについて、その基本から応用、そして様々なCSSセレクタの活用方法を詳細に解説しました。
querySelector
は、引数に指定されたCSSセレクタに一致する最初の要素を返します。要素が見つからない場合はnull
を返します。このメソッドの最大の強みは、Webサイトのデザインに使用するCSSセレクタと同じ豊富な表現力を利用して、目的の要素をピンポイントで指定できる点にあります。
要素セレクタ、IDセレクタ、クラスセレクタといった基本的なものから、属性セレクタ、結合子(子孫、子、兄弟)、擬似クラス(状態、構造、否定など)といった高度なセレクタまで、様々なCSSセレクタを組み合わせて使用することで、あらゆるHTML要素を効率的に取得できます。ただし、querySelector
は擬似要素(::before
, ::after
など)自体を取得することはできない点には注意が必要です。
また、querySelector
はdocument
だけでなく、特定の親要素に対しても呼び出すことで、検索範囲を限定し、コードの可読性と安全性を高めることができます。データ属性やテンプレートリテラルを組み合わせて、動的な要素取得を行うことも可能です。
複数の要素を取得したい場合は、querySelector
ではなく、同じCSSセレクタ構文を使用するquerySelectorAll
メソッドを使用します。querySelectorAll
は一致するすべての要素を静的なNodeListとして返します。単一要素が必要ならquerySelector
、複数要素が必要ならquerySelectorAll
と使い分けることが重要です。
querySelector
を使用する際は、指定したセレクタが正しいか、要素が存在するか(null
チェック)、そして「最初の要素」のみが返されるという性質を理解しておくことが、意図した通りにコードを動作させるために不可欠です。また、セマンティックで適切なHTML構造は、querySelector
による要素取得をより容易にし、コードの保守性を向上させます。
かつてのgetElementById
やgetElementsByClassName
などのメソッドも未だ有効ですが、querySelector
とquerySelectorAll
は現代のWeb開発におけるDOM操作の標準的な手法となりつつあります。これらのメソッドとCSSセレクタの知識を深めることで、JavaScriptによるインタラクティブなWebサイトやアプリケーション開発の幅が大きく広がるでしょう。
ぜひ、様々なセレクタを実際に試してみて、querySelector
の強力さと便利さを体感してください。