HTML要素の取得(セレクター編):querySelectorの活用

HTML要素の取得(セレクター編)のVSCODE画面
目次

DOMの基本構造と要素取得の重要性

ブラウザがWebページを表示する仕組み
Web開発でJavaScriptを使って画面を操作するには、まずブラウザがどうやってWebページを表示しているかを理解する必要があります。ソースによれば、WebブラウザはWebサーバーからHTML文書を受け取ると、そのテキストを解析(パース)し、**DOM(Document Object Model)**というデータ構造に変換します。このDOMは、HTMLの入れ子構造(親子関係)を、メモリ上で「ツリー構造」として表現したものです。たとえば、<html>の中に<body>があり、その中に<h1>や<ul>、さらに<li>がある…という形は、DOMツリーでは「親ノード」「子ノード」として組み立てられます。ブラウザはこのDOMツリーにCSSのルールを適用し、レイアウトを計算して画面に描画(レンダリング)します。つまり、私たちが見ているWebページの実体は、HTMLファイルそのものではなく、ブラウザが構築したDOMなのです。
JavaScriptによるDOM操作の目的
JavaScriptの主な目的は、このDOMにアクセスし、内容を書き換えてWebページの表示を動的に変えることです。ソースには「DOMが書き換えられると、自動的に画面の内容が新たなDOMに合わせて再描画される仕組みになっている」とあります。これにより、ボタンのクリックやフォーム入力があっても、ページ全体を再読み込みせずに一部だけ更新する「インタラクティブ」なWebアプリが実現します。この操作をするには、広いDOMツリーの中から、操作したい「特定の要素(ノード)」を見つける必要があります。これが「要素の取得」です。
ノードという概念
DOMツリーを作る一つ一つの部品は「ノード」と呼ばれます。種類はいくつかありますが、DOM操作で特に重要なのは次の2つです。

①要素ノード:<div>や<p>、<button>などHTMLタグに対応するノードで、属性やスタイル情報も持ちます。

②テキストノード:タグに囲まれた文字情報そのものを表すノードです。
JavaScriptで「要素を取得する」とは、正確には「DOMツリーの中から特定の要素ノードへの参照を手に入れる」ことです。取得したノードはJavaScriptのオブジェクトとして扱われ、プロパティを変えることで、間接的にHTMLの構造や見た目を操作できます。

DOM操作の第一歩
ソースでは、初心者が最初に取り組むべきステップとして「DOM操作の基本を覚える」ことが挙げられています。document.getElementByIdやquerySelectorで要素を取得して変更する練習は、自分のコードが画面に影響する様子を直接確認できるため、学習のモチベーション維持にもつながります。要素を取得できなければ、イベントも設定できず、テキストも変えられません。だからこそ、要素取得メソッドを使いこなすことは、フロントエンド開発の最も基礎で重要なスキルです。

querySelectorの導入:CSSセレクターによる検索

柔軟な検索メソッドの必要性
これまでの学習(ID編)ではgetElementByIdを扱いました。これはIDで要素を一意に特定できる強力な方法ですが、IDがない要素や、複雑な条件(例:「クラスAの中にある最初の画像」)で探したい場合には向きません。そこで登場するのがquerySelectorです。ソースでも、querySelector/querySelectorAllはgetElementByIdより「より包括的な方法」と位置づけられています。
CSSセレクターとは
querySelectorの特徴は、引数にCSSセレクターの文字列を渡せることです。CSSセレクターは、CSSでスタイルを当てる対象を指定する記法です。
・IDセレクター:#the-button
・クラスセレクター:.message
・タイプセレクター(タグ名):div, h1
・子孫セレクター:div p(divの中のp)
・属性セレクター:[type=”text”]
ソースの例にも document.querySelector(‘#the-button’) や document.querySelector(‘.box’) があり、CSSの知識をそのままDOM取得に使えるのが大きな利点です。
単一要素の取得
querySelectorは、セレクターに合う要素をDOMツリー全体(または指定範囲)から探し、最初に見つかった1つだけを返します。ソースにも「合致する要素が複数ある場合は、HTML的に一番上に書かれている要素が取得されます」とあります。たとえば.buttonが5つあっても、document.querySelector(‘.button’)が返すのは最初の1つです。複数をまとめて扱うなら、後述のquerySelectorAllが必要です。
戻り値の処理
見つかれば要素ノード(オブジェクト)が返り、見つからなければnullが返ります。getElementByIdと同様、nullチェックをしてから操作するのが基本です。ソースでもJavaScriptの「ゆるさ」への言及があり、取得結果の確認は堅牢なコードを書く第一歩になります。

ID検索とセレクター検索の使い分け

getElementById vs querySelector
getElementByIdとquerySelectorはどちらも単一要素を取れますが、特徴が違います。ソースではgetElementByIdは「最も有名で入門書に最初に出るメソッド」とされ、引数にID名を渡します。一方querySelectorはCSSセレクターなので、#idを使えばID検索もできます。
const button1 = document.getElementById(‘the-button’);
const button2 = document.querySelector(‘#the-button’);
この2行は結果的に同じ要素を取得します。
使い分けの基準
どちらを使うべきかの目安は次の通りです。

①パフォーマンス:一般にgetElementByIdはIDという一意の目印を使うため高速とされます。単純なID検索なら効率的です。

②柔軟性:querySelectorはID以外も検索できます。クラスや属性などで探したいならquerySelector一択です。

③可読性と統一感:最近は一貫性を重視してquerySelectorに統一する現場もあります。


初心者への指針
ソースの「Step 2:DOM操作の基本を覚える」では、getElementByIdとquerySelectorの両方を学ぶことが推奨されています。まずは「IDが付いた重要な要素」はgetElementById、「細かい要素や条件検索」はquerySelector、と分けて考えると理解しやすいでしょう。

複数要素の取得:querySelectorAllの活用

querySelectorとの決定的な違い
「.itemを持つ要素を全部まとめて操作したい」場合、querySelectorでは最初の1つしか取れません。こういうときに使うのがquerySelectorAllです。ソースによれば、querySelectorAllは「セレクターに合致する複数の要素を返却します」。戻り値は1要素ではなくNodeListというオブジェクトになります。
NodeListとは
NodeListは、取得した要素ノードを並べたリスト(コレクション)です。ソースでも「配列のようなイメージだが厳密には違う」と注意されています。インデックスで取り出したりlengthで数を見たりはできますが、配列のmap/filter/reduceなどが直接使えない場合があります。
具体的な使用例
const buttons = document.querySelectorAll(‘.button’);
これで.buttonを持つ要素がすべてbuttonsに入ります。該当が0件でもnullではなく「空のNodeList」が返ります。ここはquerySelector(見つからないとnull)との大きな違いで、バグになりやすい点です。querySelectorAllは結果が「集合」だと意識し、個々を操作するには次章のループが必要だと理解しておきましょう。

NodeListの操作:ループ処理による一括操作

ループ処理の必要性
querySelectorAllで取ったNodeListに対して、リストのままstyleを変えることはできません。
const boxes = document.querySelectorAll(‘.box’);
boxes.style.color = ‘red’; // エラーになるか、動作しません
boxesは要素ではなく「要素のリスト」なので、リスト自体にstyleがないからです。中身の要素を1つずつ取り出して操作する必要があります。
forループによる操作
基本はforループです。ソースでも紹介されています。
for (let i = 0; i < listItems.length; i++) {
console.log(listItems[i].id);
}
NodeListはインデックスでアクセスできるので、forで順番に処理できます。基本文法だけで書けるため互換性も高いです。
forEachメソッドの利用
最近(ES6以降)はNodeListにもforEachが使えるブラウザが多く、次のように書けます。
listItems.forEach(item => {
console.log(item.id);
});
アロー関数と組み合わせると読みやすくなります。ただし古いブラウザ(IEなど)では使えない場合がある点に注意が必要です。
配列への変換
mapやfilterなど配列メソッドを使いたいなら、NodeListを配列に変換します。ソースでは次の方法が紹介されています。

①Array.from()やsliceで変換:Array.prototype.slice.call(listItems, 0)のようにして変換します。

②スプレッド構文で変換:[…listItems]という記述で、NodeListを展開して新しい配列を作成します。 一度配列に変換してしまえば、JavaScriptの豊富な配列メソッドを自由に活用でき、DOM要素のフィルタリングや加工が容易になります。

要素の探索範囲:document以外の起点

ドキュメント全体以外からの検索
これまでdocument.querySelectorのようにdocument(ページ全体)を起点に探してきましたが、querySelectorは要素に対しても使えます。ソースには、.boxを取ったあと、その中から.buttonを探す例があります。
const box = document.querySelector(‘.box’);
const button = box.querySelector(‘.button’);
2行目は「boxの子孫の中から.buttonを探す」という意味です。
スコープを限定するメリット
検索範囲を狭めるメリットは次の通りです。

①パフォーマンス:全体検索より範囲が小さく、効率が良い場合がある

②精度:精度の向上: 同じクラス名がページのあちこちで使われている場合、特定の親要素配下のものだけを確実に取得できます。例えば、「ヘッダーの中のボタン」と「フッターの中のボタン」を区別したい場合に有効です。


DOMツリーの親子関係
これはDOMがツリー構造であることを利用しています。documentは根ですが、取得した要素も「その下の部分ツリーの根」として振る舞えます。ソースでも「document以外に対して呼ぶと子要素から検索される」とあり、コンポーネント単位でDOM操作する際によく使われます。

DOMツリーのトラバース:親・子・兄弟要素へのアクセス

関係性に基づく移動
querySelectorは条件で要素を探す方法ですが、いま持っている要素を基準に「親」「隣」へ移動したいこともあります。これがDOMトラバースです。
親要素の取得
親要素はparentNodeで取れます。
const button = document.querySelector(‘.button’);
const box = button.parentNode;
たとえば「削除ボタンが押されたら、そのボタンを含む親要素ごと消す」といった処理でよく使います。
子要素の取得
子要素はchildrenやchildNodesで取れます。ソースによれば、childrenはHTML要素だけの一覧(HTMLCollection)、childNodesは改行などのテキストノードも含みます。基本的にタグを操作したいならchildrenが便利です。
兄弟要素の取得
兄弟要素はnextElementSibling(次)やpreviousElementSibling(前)で取れます。ソースではnextSiblingだと空白のテキストノードを拾う可能性があるため、要素だけ欲しいならElement付き(nextElementSibling)を使うべきだと説明されています。

クラス属性とカスタムデータ属性の操作

classListによるスタイルの切り替え
要素を取った後、見た目を変える基本はクラス操作です。ソースではclassListで追加・削除・切り替えができると紹介されています。
・element.classList.add(‘is-active’):追加
・element.classList.remove(‘is-active’):削除
・element.classList.toggle(‘is-active’):切り替え
・element.classList.contains(‘is-active’):確認
setAttribute(‘class’, ‘…’)で上書きもできますが、既存クラスを消したり文字列処理が面倒になったりするので、classListが適切です。
カスタムデータ属性(dataset)の活用
JavaScript用のデータをHTMLに持たせたいときはdata-*属性を使います。ソースによれば、HTMLに

と書くと、datasetで読めます。
const element = document.querySelector(‘#the-item’);
console.log(element.dataset.myName); // “john”
HTML側はケバブケース(data-my-name)、JavaScript側はキャメルケース(myName)に自動変換される点に注意しましょう。IDやクラスは識別やスタイル、アプリ固有データはdatasetという使い分けが重要です。

イベントリスナーとの連携:操作のトリガー

要素取得からイベント設定への流れ
querySelectorで要素を取るのはゴールではなくスタートです。取った要素にイベントリスナーを付けて、ユーザー操作(クリック・入力)をきっかけに処理を動かすことで、初めて動くWebページになります。ソースでもaddEventListenerの基本が紹介されています。
const button = document.querySelector(“button”);
button.addEventListener(“click”, () => {
alert(“ボタンが押されました!”);
});
これは「最初のbuttonを取得し、クリックされたらアラートを出す」という処理です。
イベント駆動型プログラミング
JavaScriptはイベント駆動に強い言語です。操作というイベントが発生した瞬間に、登録した関数(コールバック)を実行します。コールバックの中でさらにquerySelectorで別要素を取り、classListやtextContentで画面を更新する、という流れがフロントエンドの基本サイクルです。

実践的なDOM操作:まとめと学習ステップ

DOM操作の全体像
ここまで見てきた通り、DOM操作は以下のステップで成り立っています。

  1. 取得: querySelector / querySelectorAll / getElementById でターゲットを特定する。
  2. 監視: addEventListener でユーザーのアクションを待ち受ける。
  3. 操作: イベント発生時に、クラス操作(classList)、テキスト変更(textContent)、要素の生成・挿入(createElement, appendChild)を行う。
    サーバーサイドJSとの違い
    学習を進める上で、JavaScriptには「ブラウザで動くもの」と「サーバー(Node.js)で動くもの」があることを理解しておく必要があります。 ソースにあるように、DOM操作(documentオブジェクトの使用)はブラウザ環境特有の機能です。Node.js環境ではdocumentやwindowは存在せず、代わりにファイルシステム操作などのAPIが提供されています。 「JavaScriptを学ぶ」といっても、実行環境によって使える機能(API)が異なるため、今自分が書いているコードがどこで動くものなのかを意識することが大切です
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次