はじめに
querySelectorは、CSSセレクタの書き方でHTML要素を取得できるJavaScriptのメソッドです。DOM操作の入口になるため、CSSの理解と組み合わせると、Web開発で必要になる表示切り替え、フォーム制御、クラス操作を短いコードで扱えます。
初心者向けに押さえたい結論は、単一要素ならdocument.querySelector()、複数要素ならdocument.querySelectorAll()を使い分ける点です。ただし、querySelectorは一致した最初の要素だけを返すため、戻り値がnullになる場合の分岐も同時に考える必要があります。
- HTML Living Standard / CSS Cascading and Inheritance Level 5
- JavaScript ECMAScript 2024 相当
- Google Chrome 126 / Firefox 127 / Safari 17.5
querySelectorとCSSセレクタの関係- 単一要素と複数要素を取得するサンプルコードの読み方
nullやセレクタエラーを避ける考え方- クラス操作、イベント処理、フォーム追加への応用
- プロフェッショナルなWeb開発で使いやすいコーディング技術
CSSとは何か
CSSは、HTMLで作った構造に見た目を与えるためのスタイル言語です。文字色はcolor、文字サイズはfont-size、余白はmarginやpaddingで調整し、レイアウトはdisplayやgrid-template-columnsなどで組み立てます。
そのCSSで使うセレクタは、querySelectorの引数にもそのまま近い形で渡せます。たとえば.cardはクラス、#mainはID、article pは子孫関係を表すため、CSSを学ぶことはquerySelectorの精度を高める作業にもなるのが基本です。
公式ドキュメントで仕様を確認する場合は、MDNのDocument.querySelector()とMDNのCSS selectorsが一次情報に近い資料として参照しやすいです。HTML側の要素構造は、WHATWG HTML Living Standardでも確認できます。
CSSの基本概念
CSSは、セレクタと宣言ブロックで構成されます。セレクタが対象を決め、宣言ブロック内のpropertyとvalueが見た目を変える、という関係で整理すると理解しやすくなるのが目安です。
結果: 期待される表示は、<p>要素の文字色が赤くなり、文字サイズが16pxになる状態です。
このサンプルコードでは、pがセレクタ、colorとfont-sizeがプロパティです。その同じpをquerySelectorに渡すと、JavaScriptから最初の段落要素へアクセスできます。
結果: 期待される結果は、クラス名exampleを持つ最初の要素が変数elemに入ることです。該当要素がなければnullになります。
これにより、CSSで見た目を決めた要素を、JavaScript側でも同じセレクタ感覚で扱えますし、ここがポイントです。初心者向けのプログラミング学習では、HTML、CSS、querySelectorを切り離さずに読むほうが、DOM操作の流れをつかみやすいでしょう。
💡 Tips:querySelectorの引数は文字列です。'.example'のように引用符で囲み、CSSセレクタとして成立する形にするのが基本です。
querySelector()とは
querySelector()は、指定したCSSセレクタに一致する最初のElementを返すDOM APIです。該当する要素が見つからない場合、戻り値はnullになります。
この性質から、querySelectorは「ページ内で対象がひとつに決まる操作」に向いているのがポイントです。見出しの差し替え、ボタンへのイベント登録、フォーム全体の取得など、対象が明確な処理で読みやすいコードになります。
一方、同じ条件に合う要素をすべて扱うならquerySelectorAll()を選びますし、ここがポイントです。querySelectorとquerySelectorAllの違いを早い段階で整理しておくと、Web開発のサンプルコードを読むときに迷いにくくなるのが一般的です。
querySelector()の基本的な概念
基本的にquerySelectorでは、.className、#idName、tagName、[data-role='button']のようなCSSセレクタを使います。CSS側で対象を絞り込む感覚と同じなので、コーディング技術として覚えやすいAPIです。
具体的には、div.example pのような子孫セレクタも使えます。このときquerySelectorは、DOMツリーを文書順に調べ、条件に合った最初の<p>を返するのが現実的です。
結果: 期待される結果は、div要素のうちクラス名exampleを持つ領域内から、最初の<p>要素が取得されることです。
そのため、同じ条件の要素が複数ある場合に「どれが取れるのか」をHTMLの並び順から判断する必要があるのが目安です。プロフェッショナルな実装では、対象がひとつだと分かるidや専用のdata-*属性を用意する設計もよく使われます。
querySelector()の基本的な構文
querySelectorの構文は短く、documentから探すか、特定の親要素から探すかで検索範囲が変わりますが、これは押さえたい点です。広い範囲を毎回探すより、親要素を絞ったほうが意図を読み取りやすいコードになります。
結果: 期待される結果は、文字列で渡したセレクタに一致する最初の要素がelementへ代入されることです。
この構文では、'セレクタ'部分に'.button'や'#app'などを入れますが、これは押さえたい点です。ただし、不正なセレクタ文字列を渡すとSyntaxErrorが発生するため、動的な値を混ぜる場合はCSS.escape()の利用も検討すると整理できます。
querySelectorはjQueryの$()とは別物です。ブラウザ標準のDOM APIなので、追加ライブラリなしで使えます。querySelector()の使い方
querySelectorの使い方は、取得したい対象をCSSセレクタで表し、その後にtextContent、classList、addEventListenerなどで操作する流れになるのがポイントです。サンプルコードを読むときは、セレクタ、戻り値、操作内容の順に追うと混乱しにくいです。
これから扱う例は、初心者向けの基本からプロフェッショナルなWeb開発でも使う考え方までつながっていると理解できます。単一要素、複数要素、クラス、ID、属性の順で押さえると、CSSセレクタの知識がそのままプログラミングに結びつきます。
サンプルコード1:単一要素の選択
単一要素を選ぶ場面では、querySelectorが最も読みやすい選択肢になるのが一般的です。HTML内に同じタグが複数あっても、親要素やクラスを組み合わせることで狙った範囲を絞れますし、これが一つの目安です。
結果: 期待される表示は、containerクラスの中に段落が2件並ぶHTML構造です。
結果: 期待される結果は、.container内にある最初の<p>がfirstParagraphへ入ることです。
このとき、querySelectorは2件目の段落を返しません。2件目も含めて扱うならquerySelectorAllに切り替え、forEach()で順番に処理します。
サンプルコード2:複数要素の選択
複数要素の取得にはquerySelectorAll()を使います。名前は似ていますが、querySelectorは単一要素、querySelectorAllはNodeListを返すという違いがあると覚えるとよいでしょう。
結果: 期待される結果は、.container内のすべての<p>要素がNodeListとしてallParagraphsに入ることです。
このNodeListは配列に似ていますが、必要に応じてArray.from()で配列へ変換できます。一般に、全件の文字色を変える、全ボタンへイベントを登録する、といった処理ではquerySelectorAllが合います。
サンプルコード3:CSSクラスによる要素の選択
CSSクラスで対象を選ぶ場合、セレクタの先頭に.を付けますが、覚えておくと役立つでしょう。クラスは複数要素に付けられるため、ひとつだけ欲しいならquerySelector、すべて欲しいならquerySelectorAllを選びますし、これが一つの目安です。
結果: 期待される結果は、クラス名importantを持つすべての要素がimportantElementsへ入ることです。
そのクラスがCSSの装飾用なのか、JavaScriptの取得用なのかを分けたい場合は、js-importantやdata-roleのような命名にする方法があります。見た目の変更でJavaScriptが壊れにくくなるため、コーディング技術として覚えておきたい考え方です。
サンプルコード4:IDによる要素の選択
IDで要素を選ぶ場合、セレクタの先頭に#を付けますし、ここを基本と考えるとよいでしょう。HTML上でIDは同一ページ内に同じ値を重複させない設計が前提なので、querySelectorとの相性が良い対象です。
結果: 期待される結果は、IDがmainの要素がmainElementへ入ることです。対象がなければnullになります。
ただし、IDを多用しすぎるとCSSの詳細度が高くなり、後からスタイルを調整しにくくなる場合があります。見た目はクラス、ひとつのアプリ領域やフォームはID、という使い分けが現実的です。
サンプルコード5:属性値による要素の選択
属性セレクタを使うと、data-roleやaria-expandedの値に応じて対象を選べますし、ここがポイントです。CSSの装飾とJavaScriptの責務を分けたい場合、data-*属性は扱いやすい選択肢になるのが現実的です。
結果: 期待される結果は、data-role属性の値がbuttonであるすべての要素がbuttonsに入ることです。
この書き方は、デザイン変更でクラス名が変わってもJavaScriptの対象を保ちやすい特徴があります。プロフェッショナルなWeb開発では、状態をaria-*属性で表し、動作の対象をdata-*属性で扱う設計も多く見られますが、これは押さえたい点です。
querySelectorで取得した要素がnullのままclassListやstyleへアクセスするとエラーになります。操作前に存在確認を入れるのが基本です。querySelector()の詳細な使い方
基本の取得が分かると、querySelectorは動的なUIの制御に使えると整理できます。ボタンの生成、イベント登録、クラスの追加と削除、インラインスタイルの変更、要素の複製など、Web開発のプログラミングで頻出する処理へ広がりますし、これが一つの目安です。
ただし、DOM操作を増やしすぎるとコードの見通しが悪くなる場合があります。そのため、取得処理を変数へまとめ、同じ要素へ何度もquerySelectorを呼ばない書き方を意識すると保守しやすくなります。
サンプルコード6:動的な要素の選択
動的に作った要素も、DOMへ追加された後であればquerySelectorから取得できると考えられますが、覚えておくと役立つでしょう。作成はcreateElement()、追加はappendChild()、取得はquerySelectorという流れです。
結果: 期待される結果は、body内に追加されたボタン要素がdynamicButtonとして取得されることです。
このとき、追加前にquerySelectorを実行すると対象は見つかりません。初心者向けのサンプルコードでは順番を見落としやすいため、DOMへ入った後に探す、と覚えるとよいでしょう。
サンプルコード7:イベントハンドラとの組み合わせ
ボタンを押したときの処理は、querySelectorで対象を取得してからaddEventListener()を登録します。クリック、入力、送信などのイベントは、UIを動かすプログラミングの中心になります。
結果: 期待される動作は、IDがmyButtonの要素をクリックしたときにアラートが開くことです。
ただし、対象が存在しない可能性がある画面では、if (button)で囲んでからイベントを登録すると言えるでしょう。これにより、別ページで同じJavaScriptを読み込んでもTypeErrorを避けやすくなります。
サンプルコード8:クラスの追加と削除
表示状態の切り替えには、直接styleを書き換えるよりclassListでCSSクラスを切り替える方法が扱いやすいです。CSS側に状態の見た目をまとめ、JavaScript側は状態名を付け替えるだけにできます。
結果: 期待される結果は、.my-elementの要素にnew-classが追加され、old-classが外れることです。
この形なら、見た目の変更はCSSファイルに集約できるのが基本です。プロフェッショナルなコーディング技術では、JavaScriptに色や余白を直接散らさず、状態管理に寄せるほうが保守しやすいと言えます。
サンプルコード9:スタイルの動的変更
一時的な見た目の変更には、styleプロパティも使えます。単発のデモや小さなUIでは短く書けますが、規模が大きいWeb開発ではCSSクラスへの移管も検討するのが目安です。
結果: 期待される表示は、.my-elementの背景色が青、文字色が白になる状態です。
この例では、CSSのbackground-colorがJavaScriptではbackgroundColorになります。ハイフン区切りのCSSプロパティがキャメルケースに変わる点は、初心者向けの学習でつまずきやすい箇所です。
サンプルコード10:要素の複製と移動
既存要素を再利用したい場合、cloneNode()で複製してから別の場所へ追加できます。querySelectorは複製元の特定に使い、追加先にはappendChild()やappend()を使いるのがポイントです。
結果: 期待される結果は、.original-elementの複製が作成され、bodyの末尾に追加されることです。
その複製に同じidが含まれている場合、HTML上の一意性が崩れるおそれがあります。カードやフォーム部品を複製するときは、IDを使わずクラスやdata-indexで管理する設計が現実的です。
querySelector()でよくあるエラーと対処法
querySelectorで多い問題は、対象が見つからない、不正なセレクタを渡す、DOM生成のタイミングが合わない、という流れで起きます。どれも戻り値と実行順序を確認すれば切り分けやすくなるのが一般的です。
一般に、エラーを避けるにはnullチェック、セレクタ文字列の確認、DOMContentLoaded後の実行が有効です。サンプルコードを実務へ移す際は、この保護処理を省かないほうが安定すると理解できます。
エラー例1:セレクタが見つからない
指定したセレクタに一致する要素がない場合、querySelectorはnullを返します。そのままnull.classListのように扱うと、Cannot read properties of null系のエラーになるのが現実的です。
そのため、取得後に条件分岐を入れます。対象が存在するときだけ処理を進める形にすると、ページごとにHTML構造が違うサイトでも壊れにくくなると覚えるとよいでしょう。
結果: 期待される結果は、.panelが存在する場合だけis-activeクラスが追加されることです。存在しない場合は何も処理しません。
エラー例2:非互換または不正なセレクタ使用
セレクタ文字列がCSSセレクタとして不正な場合、querySelectorはSyntaxErrorを投げます。ユーザー入力や外部データをIDに混ぜる処理では、文字列のエスケープを考える必要があります。
このとき役立つのがCSS.escape()です。公式ドキュメントによれば、CSSセレクタ内で安全に使える文字列へ変換するためのメソッドとして用意されていると整理できると考えられます。
結果: 期待される結果は、特殊文字を含むID値でもセレクタとして組み立てやすくなり、該当要素があればitemに入ることです。
ただし、CSS.escape()の対応状況はブラウザごとに確認します。互換性が気になる場合は、対象ブラウザのサポート範囲をMDNやCan I useなどで確認するのが実用的です。
エラー例3:予期せぬnull値
セレクタが正しくても、JavaScriptの実行がHTMLの読み込みより早いとquerySelectorは対象を見つけられません。<script>を<head>内で読み込む構成では、特に発生しやすい問題です。
その場合は、defer属性を使うか、DOMContentLoadedイベント後に処理を走らせます。HTMLの構築が終わってからquerySelectorを呼ぶため、対象取得の成功率が上がりますが、覚えておくと役立つでしょう。
結果: 期待される表示は、DOM構築後に.targetが存在する場合、そのテキストが「準備完了」に変わる状態です。
querySelector()の応用例
querySelectorの応用では、UIの状態をどこで持つかが焦点になります。ナビゲーション、タブ、フォーム、ギャラリーのような部品では、HTML構造、CSSクラス、JavaScriptの取得対象をそろえると読みやすくなると理解できると言えるでしょう。
こうした部品は、初心者向けのサンプルコードとしても、プロフェッショナルなWeb開発の土台としても役立ちます。小さな部品を正しく作れると、複雑な画面でも同じ発想を使い回せます。
サンプルコード11:レスポンシブナビゲーションバー
レスポンシブ対応では、画面幅に応じてナビゲーションの表示を変える場面があると覚えるとよいでしょう。CSSのmedia queryで済む場合はCSS側に任せ、JavaScriptで状態を持つ場合にquerySelectorを使いるのが基本です。
結果: 期待される表示は、ウィンドウ幅が600ピクセル未満のとき.navbarが非表示になり、それ以上で表示される状態です。
一方、この処理はCSSの@mediaでも表現できます。JavaScriptが必要なのは、開閉ボタンの状態やaria-expandedも同時に変えたい場合だと整理できます。
サンプルコード12:タブコンテンツの切り替え
タブUIでは、クリックされた要素にactiveクラスを付け、元の要素から外す処理が中心になると考えられますし、ここを基本と考えるとよいでしょう。querySelectorは現在アクティブな要素を探す用途に向いています。
結果: 期待される動作は、クリックしたタブにactiveクラスが移り、以前のアクティブタブから同クラスが外れることです。
ただし、初期状態で.activeが存在しないとエラーになります。実用コードではlet activeTab = document.querySelector('.active');の後に存在確認を入れるか、HTML側で初期タブを必ず用意します。
サンプルコード13:動的なフォーム要素の追加
フォームに入力欄を追加する処理では、追加ボタンとフォーム本体をそれぞれquerySelectorで取得すると言えるでしょう。クリックイベント内でcreateElement()を呼び、生成したinputをフォームへ追加するのが目安です。
結果: 期待される動作は、IDがaddButtonの要素をクリックするたびに、#myFormへテキスト入力欄が追加されることです。
このコードを実務寄りにするなら、labelの追加、name属性の一意化、削除ボタンの用意も検討します。アクセシビリティを考えると、入力欄だけを増やすより関連する説明も一緒に生成するほうが扱いやすいです。
サンプルコード14:ギャラリーの動的な生成
外部データから画像一覧を作る場合、fetch()でJSONを取得し、querySelectorで取得したコンテナへimg要素を追加します。非同期処理が混ざるため、失敗時の分岐も考えますし、ここを基本と考えるとよいでしょう。
結果: 期待される表示は、取得した画像データのurlを使って<img>要素が作られ、#gallery内へ順番に追加される状態です。
そのまま使う場合でも、実際のAPIではalt属性、通信失敗時のcatch()、読み込み中の表示を加えます。プロフェッショナルなWeb開発では、見た目のCSSと非同期の状態管理を分けて考えるのが現実的です。
CSSとquerySelector()を使ったコーディングのコツ
CSSとquerySelectorを組み合わせるときは、セレクタを短く保ち、役割を混ぜすぎないことが大切です。装飾用クラス、JavaScript取得用の属性、状態を表すクラスを分けると、変更に強いコードになります。
特に押さえたいのは、DOM取得を何度も書かず、必要な要素を変数へ保存する点です。同じquerySelectorを繰り返すより、constで参照を持ったほうが処理の意図も読み取りやすくなるのが基本です。
| 対象 | セレクタ例 | 使うAPI | 注意点 | 主な用途 |
|---|---|---|---|---|
| 単一ID | #main | querySelector | ID重複を避ける | 主要領域の取得 |
| 単一クラス | .hero | querySelector | 最初の一致だけ返る | 代表要素の操作 |
| 複数クラス | .card | querySelectorAll | NodeListを処理する | カード一覧 |
| タグ | button | querySelector | 範囲が広くなりやすい | 単純なデモ |
| 子孫 | .menu a | querySelectorAll | 構造変更に注意 | ナビリンク |
| 直接子 | .list > li | querySelectorAll | 階層を固定する | 直下項目 |
| 属性 | [data-role='tab'] | querySelectorAll | 値の表記を統一 | JS対象の分離 |
| 状態属性 | [aria-expanded='true'] | querySelector | アクセシビリティと連動 | 開閉状態 |
| 疑似クラス | li:first-child | querySelector | 意図をコメントで補う | 先頭要素 |
| 否定 | .item:not(.disabled) | querySelectorAll | 条件を複雑にしすぎない | 有効項目 |
| フォーム | input[name='email'] | querySelector | nameの変更に注意 | 入力値取得 |
| ボタン | [data-action='save'] | querySelector | 処理名を明確にする | 保存操作 |
| モーダル | #modal | querySelector | 表示状態をクラス化 | 開閉UI |
| タブ | .tab.active | querySelector | 初期状態を置く | 選択中取得 |
| 一覧 | .todo-item | querySelectorAll | 追加後の取得に注意 | リスト処理 |
| 親内検索 | panel.querySelector('.title') | querySelector | 検索範囲を狭める | 部品単位 |
| テンプレート | template | querySelector | contentを扱う | 複製元 |
| 画像 | img[data-src] | querySelectorAll | altも考える | 遅延読込 |
| エラー表示 | .error-message | querySelector | 空文字と非表示を分ける | バリデーション |
| チェック項目 | input:checked | querySelectorAll | 状態が変わる | 選択値 |
| 無効要素 | button:disabled | querySelectorAll | UI状態と同期 | 送信制御 |
| リンク | a[target='_blank'] | querySelectorAll | relを確認 | 外部リンク |
| 本文 | article p | querySelectorAll | 対象が多くなりやすい | 文章装飾 |
| 見出し | h2 | querySelectorAll | 目次生成で使う | ナビ作成 |
| 通知 | [role='alert'] | querySelector | 支援技術への伝達 | メッセージ |
| 開閉部品 | details[open] | querySelectorAll | 状態変化を読む | アコーディオン |
| カスタム属性 | [data-id] | querySelectorAll | ID値の重複に注意 | データ紐付け |
| 検索範囲 | root.querySelector() | querySelector | documentに固定しない | コンポーネント |
| 未選択 | .item:not(.selected) | querySelectorAll | 状態名を統一 | 一括処理 |
| 安全化 | CSS.escape(id) | querySelector | 動的値を保護 | 外部入力 |
クリーンなコードを書く
クリーンなコードでは、querySelectorのセレクタが短く、対象が読み取れる名前になっています。.content div ul li spanのように深いセレクタへ依存すると、HTMLを少し変えただけで動かなくなるかもしれません。
そのため、JavaScript用のdata-js属性やdata-action属性を使う方法があります。CSSの見た目用クラスとプログラミング用の取得対象を分けることで、デザイン変更と動作変更を独立させやすくなるのが目安です。
同様に、繰り返し使う処理は関数へ分けますし、ここがポイントです。たとえばopenModal()、closeModal()、toggleMenu()のように命名すると、querySelectorで取得した要素がどの操作に使われるのか把握しやすくなります。
パフォーマンスを意識する
パフォーマンス面では、同じ要素を何度も検索しないことが基本になるのがポイントです。querySelector自体は一般的な用途で扱いやすいAPIですが、ループ内で毎回DOM全体を探す書き方は避けたほうがよいでしょう。
具体的には、親要素を先に取得してから、その中でparent.querySelector()を使います。検索範囲が狭くなるだけでなく、コードを読む側にも「この部品内だけを操作する」という意図が伝わりますが、これは押さえたい点です。
イベント処理が多い一覧では、各要素へ個別にaddEventListenerを付けるより、親要素でクリックを受けるイベント委譲が向く場合があるのが一般的です。この場合も、クリックされた要素の判定にclosest()やmatches()を組み合わせると柔軟です。
💡 Tips: CSSだけで表現できる表示切り替えはCSSへ寄せ、JavaScriptは状態変更に集中させると、CSS、サンプルコード、プログラミングの責務が整理されます。
まとめ
querySelectorは、CSSセレクタを使ってDOM要素を取得するための標準的なメソッドです。単一要素にはquerySelector、複数要素にはquerySelectorAllを使い、戻り値がnullになる可能性まで含めて設計すると安定します。
CSSのセレクタを理解しているほど、querySelectorの指定も正確になるのが現実的です。クラス、ID、属性、疑似クラスを使い分ければ、初心者向けの小さなサンプルコードからプロフェッショナルなWeb開発まで同じ考え方で展開できるのがポイントです。
一方、JavaScriptに見た目の値を詰め込みすぎると、後から修正しにくくなります。表示状態はCSSクラスで表し、DOM取得にはdata-*属性を使い、イベントや非同期処理では存在確認を入れる、という形が実用的です。
こうしたコーディング技術を積み重ねると、querySelectorは単なる取得メソッドではなく、HTML、CSS、プログラミングを結ぶ接点として機能すると整理できます。サンプルコードを写すだけでなく、どのセレクタが何を選び、どの状態を変えているのかを読む姿勢が理解を深めますし、これが一つの目安です。
※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。


