【JavaScript】子要素を取得する方法を初心者向けに解説
はじめに
Webページを作成していると、特定の要素の中にある子要素を取得したいケースは多いのではないでしょうか。
たとえば、メニューリストの各項目を取り出して色を変えたり、テーブルの行を順番に読み込んで合計値を計算したりといった場面が挙げられます。
JavaScriptを使うことで、意図したとおりに要素同士の関係を扱い、動きのあるページを実装できます。
しかし、DOM(Document Object Model)をしっかり理解していないと、「なぜ思ったとおりに子要素を取り出せないのか」「どのメソッドを使えばスマートに書けるのか」といった疑問やトラブルに直面するかもしれません。
そこで本記事では、JavaScriptで子要素を取得する方法をなるべくわかりやすく丁寧に解説していきます。
DOM構造の基本から、さまざまなメソッドの使い分け方法、さらに実務でも活かせる具体的なサンプルコードまで取り上げます。
初心者の皆さんにも読んでいただきやすいよう、専門用語はなるべく平易に説明し、実務で想定される場面と紐付けて話を進めます。
この記事を読むとわかること
- DOM操作の基本と、子要素を取得する必要性
- よく使われるプロパティやメソッド(
children
やchildNodes
、querySelectorAll
など) - 子要素の取得や操作を行う具体的なサンプルコード
- 実務での活用シーンや気をつけるポイント
- よくあるエラーや疑問点の対処方法
JavaScriptでDOMを扱う基本
DOMとは何か
Webブラウザは、HTMLファイルを「DOM」と呼ばれるツリー構造として解釈します。
たとえば、<body>
要素の中に <div>
があり、その中に <p>
や <img>
があるといった具合に、入れ子状の階層構造が形成されるわけです。
JavaScriptでは、このDOMを操作して、HTML要素の取得や中身の書き換えなどを行います。
DOMを活用することで、ページの見た目や振る舞いを変更できるようになります。
DOMの仕組み
DOMは、HTMLタグを「ノード」としてツリー状に配置したものです。
ノードには大きく分けて、要素ノードとテキストノード、コメントノードなどがあります。
- 要素ノード:
<div>
や<span>
などのタグに相当します。 - テキストノード: 文字列の実体部分です。改行や空白のような目に見えない文字もテキストノードに含まれる場合があります。
- コメントノード:
<!-- -->
のようなHTMLコメントが該当します。
本記事の中心となる「子要素を取得する」という操作では、要素ノードかテキストノードかを把握しておくことが重要です。
なぜなら、子要素を取得するときに、テキストノードまで配列やリストに含まれてしまうケースがあるからです。
子要素に注目する理由
子要素は、ある要素の内部に直接含まれるノードたちを指します。
たとえば、以下のようなHTML構造を考えてみます。
<div id="container"> <p>最初の段落</p> <p>2番目の段落</p> </div>
ここで id="container"
が付いた <div>
要素の子要素は、ふたつの <p>
要素です。
また、<p>
要素の内部にはテキストノードが含まれているため、そのテキスト自体をどう扱いたいかによって書き方が変わることがあります。
JavaScriptで子要素を取得できるようになると、こうしたDOM操作の具体的なアクションをスムーズに実装できます。
子要素を取得する方法一覧
要素ノードとテキストノードの違い
先ほど述べたとおり、DOMにはさまざまな種類のノードがあります。
初心者の皆さんが混乱しがちなのが、「空白や改行もテキストノードとして扱われる場合がある」という点です。
たとえば、DOMを構造的に確認できるデバッガーなどを使うと、思いもよらないテキストノードが表示されることもあります。
もし「子要素」を取得するつもりが、「テキストノードまで含まれてしまった」という状況が起きると、予想外の動作を引き起こすこともあるでしょう。
この違いを理解していれば、適切なメソッドやプロパティを使う際に迷いにくくなります。
.children
と .childNodes
の違い
要素ノードの子要素を取得するときに、children
と childNodes
という2種類のプロパティがあります。
children
: 要素ノード(HTMLタグ)だけを取得します。テキストノードやコメントノードは含まれません。childNodes
: テキストノードやコメントノードを含む、すべての子ノードを取得します。
実務では、たとえば <ul>
の子要素である <li>
のみを扱いたいようなケースが多いでしょう。
その際、children
を使えば余計なテキストノードが混ざらず、よりシンプルに要素を取り出せます。
以下は簡単な例です。
<ul id="myList"> <li>アップル</li> <li>バナナ</li> <li>オレンジ</li> </ul> <script> const list = document.getElementById("myList"); console.log(list.children); // HTMLCollection(3) -> [li, li, li] console.log(list.childNodes); // NodeList(7) -> #text, li, #text, li, #text, li, #text </script>
ここで .children
は <li>
だけを取得し、.childNodes
はテキストノードや改行を含むすべてを取得しています。
どちらを使えば良いかは、実現したい動作によって変わるため、状況に応じて使い分けるとよいでしょう。
querySelector
や querySelectorAll
を使った取得
子要素を取得する場面でも、querySelector
や querySelectorAll
は強力な方法です。
これらはCSSセレクタを指定できるため、複雑な構造からも狙い通りに要素をピンポイントで取得できます。
たとえば、ある要素の下にある特定のクラスを持つ子要素だけを取得したい場合、次のように書けます。
<div class="wrapper" id="mainWrapper"> <p class="highlight">ここはハイライト</p> <p>これは通常の段落</p> <span class="highlight">別のハイライト</span> </div> <script> const wrapper = document.getElementById("mainWrapper"); const highlights = wrapper.querySelectorAll(".highlight"); console.log(highlights); // NodeList(2) -> [p.highlight, span.highlight] </script>
querySelectorAll
は複数の要素を取得するため、リストのように扱えます。
一方で、先頭の1つだけ欲しい場合は querySelector
を使います。
CSSセレクタで子孫要素を柔軟に指定できるのがポイントです。
>
(子孫ではなく直接の子要素を指定)や span.highlight
(タグとクラスの組み合わせ)など、多様なセレクタを組み合わせると便利でしょう。
getElementsByTagName
や getElementsByClassName
の利用
やや古めかしい書き方にも見えますが、getElementsByTagName
や getElementsByClassName
もよく使われるメソッドです。
getElementsByTagName("li")
のようにタグ名を指定して取得getElementsByClassName("highlight")
のようにクラス名を指定して取得
これらのメソッドはライブリストを返すことがあるため、DOMの変化に合わせて結果が動的に変わる場合があります。
ただし、最近の学習の流れでは、必要に応じて querySelectorAll
を使うほうが直感的に書きやすいケースも多いかもしれません。
いずれにしても、多くのプロジェクトで目にするメソッドであることは間違いないので、頭の片隅に置いておくと良いでしょう。
実務での活用例
ナビゲーションメニューの子要素を操作する
Webサイトのヘッダーにあるナビゲーションメニューを制御する場面はよくあります。
ユーザーが現在どのページにいるかでメニューをハイライトしたり、動的にメニューを切り替えたりしたいこともあるでしょう。
たとえば、メニューを囲んでいる <ul>
の子要素(<li>
)を一括で取得して、その中から該当するメニューアイテムだけクラスを付与するといった操作が必要になります。
このとき .children
を使えば、余計なテキストノードを扱わずに済むので便利です。
フォーム入力欄の動的な検証
フォームに複数の入力欄があり、それぞれに対して入力チェックをするケースもあるでしょう。
実装例として、フォームの子要素としてたくさんの <input>
があるときに、まとめて取得し、値を読み取ってエラーメッセージを表示するような動きが挙げられます。
.children
や querySelectorAll("input")
を用いて取得後、for...of
や forEach
といったループで回して、バリデーションロジックを適用します。
こうした操作は実務でもよく登場するため、理解しておくと便利です。
特に大量の入力欄や複雑なレイアウトを扱うとき、子要素をきちんと把握するだけで、ロジックが格段にシンプルになります。
いろいろなパターンのサンプルコード
ここでは、もう少し具体的なコード例を見ていきましょう。
実務でよくありそうなケースを想定して、HTML構造とJavaScriptの両方を整理してみます。
特定の子要素だけを取得する
HTML構造例
<div id="articleSection"> <h2>見出し1</h2> <p>文章1</p> <h2>見出し2</h2> <p class="highlight">文章2</p> <h2>見出し3</h2> <p>文章3</p> </div>
ここでは、<h2>
要素だけ、あるいは .highlight
を含む <p>
だけを取得したいと考えます。
JavaScriptコード例
const section = document.getElementById("articleSection"); // 子要素のうち、h2タグだけを取得 const headings = section.getElementsByTagName("h2"); for (const heading of headings) { console.log(heading.textContent); } // CSSセレクタで取得する場合 const highlightParagraph = section.querySelector("p.highlight"); console.log(highlightParagraph.textContent);
上記の例では、HTMLコレクションや単一の要素を取得し、それぞれに対して textContent
を表示しています。
実務では、ここで文字色を変えたりクラスを追加してスタイルを変えたりすることが多いでしょう。
forループで複数の子要素をまとめて操作
HTML構造例
<ul id="menuList"> <li>トップページ</li> <li>サービス一覧</li> <li>お問い合わせ</li> </ul>
JavaScriptコード例
const menu = document.getElementById("menuList"); const menuItems = menu.children; // <li>だけ取得 for (let i = 0; i < menuItems.length; i++) { const item = menuItems[i]; // たとえば全項目にクラスを付与 item.classList.add("menu-item"); // 動的にテキストを変更する item.textContent = `メニュー${i + 1}: ` + item.textContent; }
このように .children
で取得した要素群をループし、各要素に対して操作を加えています。
実務では、メニューの表示・非表示を切り替える処理や、クリックイベントを付与する処理などを加える場面が想定できるでしょう。
イベントリスナーをまとめて付与する
HTML構造例
<div id="buttonContainer"> <button>ボタン1</button> <button>ボタン2</button> <button>ボタン3</button> </div>
JavaScriptコード例
const container = document.getElementById("buttonContainer"); const buttons = container.querySelectorAll("button"); buttons.forEach((btn, index) => { btn.addEventListener("click", () => { alert(`ボタン${index + 1}がクリックされました`); }); });
この例では querySelectorAll("button")
を使い、container
の子要素として存在するすべてのボタンを取得しています。
取得したボタンリストに対してループ処理を行い、それぞれにクリックイベントを登録しています。
子要素の要素数が動的に変わるケースへの対応
リアルタイムで要素が追加される場合
Webアプリケーションによっては、JavaScriptの処理によってリアルタイムで要素が追加・削除されることがあります。
たとえば、ユーザーがフォームを送信した際に、新しいコメント欄が追加されるケースです。
こうした動的な変化を追いかけるとき、単純に .children
を再取得するだけでも動きますが、さらに複雑な変更が行われる場合には管理が煩雑になることもあります。
そのため、次のような方法で対応することも検討できます。
- 要素を追加するタイミングで、すでに取得したNodeListに対して必要な処理を手動で行う
- 再度
.children
やquerySelectorAll
を呼び出して、最新のDOM状態を反映させる - 変更検知が大規模な場合、
MutationObserver
を使う
MutationObserver
の紹介
MutationObserver
は、DOMツリーが変更されたタイミングをフックできる仕組みです。
大規模なアプリケーションで「いつ追加されたか分からない子要素に対して動作を付与したい」といった状況に役立つことがあります。
具体的には、以下のような流れで使います。
MutationObserver
のインスタンスを生成する- 対象とするDOM要素と、監視するタイプ(子要素の追加・削除、属性変更など)を指定
- DOMの変化が起きると、コールバック関数で通知を受け取る
ただし、基本的なシーンではそこまで頻繁に登場しない場合もあるでしょう。
規模に応じて、単純にイベントの登録や .children
の再取得を繰り返すだけで十分なケースも多いです。
エラーやハマりがちなポイント
意図しないテキストノードの扱い
childNodes
を使った際、改行や空白がテキストノードとしてカウントされることは前述しました。
この予期せぬノードが含まれているせいで、ループ処理中に nodeName
や tagName
が読み取れずエラーになったり、textContent
をいじったときに誤った文字が入ったりすることがあります。
そうしたときには、あらかじめ .children
に切り替えるか、あるいは childNodes
の結果から要素ノードのみをフィルタリングして扱う方法を検討すると良いでしょう。
あまり意識していないと、開発中に「なんで動かないんだろう」と悩む原因になりやすいので注意が必要です。
取得結果が空になってしまう場合の確認項目
要素の取得結果が「空」だったり、undefined
や null
が返ってきたりするケースもよくあります。
原因として考えられるのは以下のような例です。
- HTMLを読み込む前にJavaScriptが実行されている(
<script>
タグの配置順問題など) id
やクラス名の指定が正しくない- そもそも該当要素が存在しない
- CSSセレクタの指定に誤りがある
もし子要素を取得しようとしてエラーになるなら、最初に親要素自体が正しく取得できているかを確かめましょう。
エラーが出る場合は、コンソールに表示されるメッセージをチェックしたり、デベロッパーツールでDOM構造を確認するのが有効です。
「一見正しいはずなのに動かない…」と感じたときは、意外と要素のIDやクラス、配置順など初歩的な設定ミスが原因になっていることが多いです。
まとめ
JavaScriptで子要素を取得する方法は、Webアプリケーションを作る上でとても重要です。
実務では、たとえば次のようなシーンで頻繁に登場するはずです。
- メニューやリストのアイテムをまとめて操作する
- フォームの入力欄の値を一括チェックする
- 見た目の変化やイベントを複数の要素に適用する
ポイントとしては、以下を押さえておくとスムーズに実装できます。
-
要素ノードだけを取得するか、テキストノードを含めるか
.children
は要素ノードのみ.childNodes
はテキストやコメントも含む
-
CSSセレクタで柔軟に指定したいなら
querySelectorAll
などを活用#id名
や.クラス名
、さらにはul > li
のような子孫セレクタも使える
-
動的に要素が変化する場合には注意
- 取得し直すか、
MutationObserver
などを使って変更を監視
- 取得し直すか、
-
誤ったセレクタやタイミングミスに気をつける
- HTMLが読み込まれる前にDOMを触ると正しく取得できない
- IDやクラス名の指定が実際のHTMLと一致しているか再確認
一度仕組みがわかってしまえば、DOM操作はさほど難しいものではありません。
ただし、コピーミスや構造の勘違いがあると、なかなか目的どおりに動かずに苦労しがちです。
本記事を参考に、ぜひ子要素の取得や操作に慣れてみてください。
DOM操作の基本的な考え方が身につくと、Webページのインタラクションや動的なUI実装がより扱いやすくなるでしょう。