リストの先頭アイテムにスタイルを適用する、テーブルの最終行を取得して合計を追加する、パンくずリストの最後の要素を太字にするなど、最初と最後の子要素を取得する操作は DOM 操作の基本です。JavaScript にはいくつかの取得方法があり、テキストノードの扱いに違いがあるため正しく使い分ける必要があります。
この記事でわかること
・firstElementChild / lastElementChild の基本(推奨)
・firstChild / lastChild との違い(テキストノード問題)
・children[0] / children[length-1] でインデックスアクセス
・querySelector の :first-child / :last-child セレクタ
・子要素が存在しない場合の null チェック
・CSS の :first-child / :last-of-type との使い分け
・リスト操作・テーブル・パンくずリストの実務パターン
・firstElementChild / lastElementChild の基本(推奨)
・firstChild / lastChild との違い(テキストノード問題)
・children[0] / children[length-1] でインデックスアクセス
・querySelector の :first-child / :last-child セレクタ
・子要素が存在しない場合の null チェック
・CSS の :first-child / :last-of-type との使い分け
・リスト操作・テーブル・パンくずリストの実務パターン
取得方法の比較
| 方法 | 最初 | 最後 | テキストノード | 推奨度 |
|---|---|---|---|---|
| Element 版 | firstElementChild |
lastElementChild |
除外 | ★★★ |
| Node 版 | firstChild |
lastChild |
含む | ★★ |
| children | children[0] |
children[length-1] |
除外 | ★★★ |
| querySelector | querySelector(":first-child") |
querySelector(":last-child") |
除外 | ★★ |
firstElementChild / lastElementChild(推奨)
最も使いやすい方法です。テキストノードやコメントノードを無視して要素ノードのみを返します。
HTML
<ul id="list"> <li>アイテム 1</li> <li>アイテム 2</li> <li>アイテム 3</li> </ul>
JavaScript
const list = document.getElementById("list");
const first = list.firstElementChild;
console.log(first.textContent); // "アイテム 1"
const last = list.lastElementChild;
console.log(last.textContent); // "アイテム 3"
子要素の数を取得する
JavaScript
// 子要素の数(要素ノードのみ) console.log(list.childElementCount); // 3 // children.length でも同じ console.log(list.children.length); // 3
firstElementChild / lastElementChild は IE9 以降のすべてのブラウザで対応しています。テキストノードの心配がないため、最も安全で推奨される方法です。firstChild / lastChild との違い(テキストノード問題)
firstChild / lastChild はテキストノードやコメントノードも含むすべてのノードを返します。HTML のインデント(空白・改行)がテキストノードとして認識されるため、意図しない結果になることがあります。
JavaScript
const list = document.getElementById("list");
// firstChild: 改行+空白のテキストノードが返る可能性
console.log(list.firstChild); // #text(改行+空白)
console.log(list.firstChild.nodeType); // 3(テキストノード)
// firstElementChild: 要素ノードのみ
console.log(list.firstElementChild); // <li>アイテム 1</li>
console.log(list.firstElementChild.nodeType); // 1(要素ノード)
nodeType の値
| nodeType | 種類 | 例 |
|---|---|---|
| 1 | 要素ノード | <div>、<li> |
| 3 | テキストノード | 改行、空白、テキスト |
| 8 | コメントノード | <!-- コメント --> |
firstChild は HTML のインデント(改行や空白)をテキストノードとして返すため、予期しない動作の原因になります。要素を取得したい場合は常に firstElementChild を使いましょう。children プロパティでインデックスアクセスする
children は要素ノードのみの HTMLCollection を返します。インデックスで任意の位置の要素にアクセスできます。
JavaScript
const list = document.getElementById("list");
// 最初の子要素
console.log(list.children[0].textContent); // "アイテム 1"
// 最後の子要素
console.log(list.children[list.children.length - 1].textContent); // "アイテム 3"
// 2番目の子要素
console.log(list.children[1].textContent); // "アイテム 2"
children はライブコレクションです。DOM の変更がリアルタイムに反映されます。firstElementChild と children[0] は同じ要素を返しますが、n 番目の要素にもアクセスできる children の方が汎用的です。querySelector で CSS セレクタを使う
JavaScript
const list = document.getElementById("list");
// :first-child → 最初の子要素
const first = list.querySelector(":first-child");
console.log(first.textContent); // "アイテム 1"
// :last-child → 最後の子要素
const last = list.querySelector(":last-child");
console.log(last.textContent); // "アイテム 3"
// 特定のタグに限定
const firstLi = list.querySelector("li:first-child");
const lastLi = list.querySelector("li:last-child");
CSS セレクタの一覧
| セレクタ | 対象 |
|---|---|
:first-child |
親の最初の子要素 |
:last-child |
親の最後の子要素 |
:first-of-type |
同じタグ種類の最初の要素 |
:last-of-type |
同じタグ種類の最後の要素 |
:only-child |
子要素が 1 つだけの場合のその要素 |
:nth-child(n) |
n 番目の子要素(1 始まり) |
:first-child と :first-of-type の違い
HTML
<div id="mixed"> <h2>見出し</h2> <p>段落1</p> <p>段落2</p> </div>
JavaScript
const box = document.getElementById("mixed");
// :first-child → 最初の子要素 = <h2>
console.log(box.querySelector(":first-child").tagName); // "H2"
// p:first-child → 最初の子要素が p か? → p ではないので null
console.log(box.querySelector("p:first-child")); // null
// p:first-of-type → 最初の p 要素
console.log(box.querySelector("p:first-of-type").textContent); // "段落1"
:first-child は「親の最初の子要素か?」を判定します。:first-of-type は「同じタグ種類の中で最初か?」を判定します。異なるタグが混在する場合に結果が変わります。子要素が存在しない場合の null チェック
子要素がない親要素で firstElementChild を参照すると null が返ります。そのまま .textContent にアクセスするとエラーになるため、必ずチェックしましょう。
JavaScript
const empty = document.getElementById("emptyList");
// NG: 子要素がないとエラー
// console.log(empty.firstElementChild.textContent);
// → TypeError: Cannot read properties of null
// OK: null チェック
const first = empty.firstElementChild;
if (first) {
console.log(first.textContent);
} else {
console.log("子要素がありません");
}
// OK: オプショナルチェーニング
console.log(empty.firstElementChild?.textContent ?? "子要素なし");
子要素の有無を確認するプロパティ
| 方法 | コード | 判定対象 |
|---|---|---|
| childElementCount | el.childElementCount > 0 |
要素ノードのみ |
| children.length | el.children.length > 0 |
要素ノードのみ |
| hasChildNodes() | el.hasChildNodes() |
テキストノードも含む |
実務でよく使うパターン
リストの先頭・末尾にスタイルを適用する
JavaScript
const list = document.getElementById("menu");
// 先頭にクラスを追加
list.firstElementChild?.classList.add("first-item");
// 末尾にクラスを追加
list.lastElementChild?.classList.add("last-item");
CSS だけで対応する方法
#menu li:first-child {
border-top: none;
}
#menu li:last-child {
border-bottom: none;
}
スタイルの適用だけなら CSS の
:first-child / :last-child で十分です。JavaScript は DOM 操作(テキスト変更・要素の追加など)が必要な場合に使います。テーブルの最初の行(ヘッダー)と最後の行を取得する
JavaScript
const tbody = document.querySelector("#dataTable tbody");
// 最初のデータ行
const firstRow = tbody.firstElementChild;
console.log(firstRow.cells[0].textContent);
// 最後のデータ行
const lastRow = tbody.lastElementChild;
console.log(lastRow.cells[0].textContent);
パンくずリストの最後の要素をリンクではなくテキストにする
JavaScript
const breadcrumb = document.querySelector(".breadcrumb");
const lastItem = breadcrumb.lastElementChild;
if (lastItem) {
// 最後のアイテムはリンクを解除してテキストのみに
const link = lastItem.querySelector("a");
if (link) {
lastItem.textContent = link.textContent;
lastItem.classList.add("current");
}
}
先頭に新しい要素を追加して古い先頭を取得する
JavaScript
const list = document.getElementById("notifications");
// 先頭に新しい通知を追加
const newItem = document.createElement("li");
newItem.textContent = "新しい通知";
list.prepend(newItem);
// 末尾の古い通知を削除(最大5件に制限)
while (list.children.length > 5) {
list.lastElementChild.remove();
}
関連記事
- 子要素を取得・操作する方法 — children・childNodes・追加・削除・置換
- 兄弟要素を取得する方法 — nextElementSibling・previousElementSibling
- n番目以外の子要素を取得する方法
- querySelector / querySelectorAll の使い方
- HTML 要素を追加・削除する方法
よくある質問
QfirstChild と firstElementChild の違いは何ですか?
A
firstChild はテキストノード(改行・空白)やコメントノードも含むすべてのノードを対象とします。firstElementChild は要素ノードのみを対象とします。通常は firstElementChild を使ってください。Q子要素が存在しない場合はどうなりますか?
A
firstElementChild / lastElementChild は子要素がない場合 null を返します。そのまま .textContent にアクセスするとエラーになるため、if (first) や el?.textContent(オプショナルチェーニング)でチェックしてください。Q:first-child と :first-of-type の違いは?
A
:first-child は「親の最初の子要素か?」を判定します。:first-of-type は「同じタグの中で最初か?」を判定します。異なるタグが混在する親要素で p:first-child と p:first-of-type は異なる結果になります。QCSS の :first-child と JavaScript の firstElementChild はどう使い分けますか?
Aスタイル変更だけなら CSS の
:first-child が最適です。JavaScript は DOM 操作(テキスト変更・要素の追加/削除・属性操作)が必要な場合に使います。Qchildren と childNodes の違いは?
A
children は要素ノードのみの HTMLCollection を返します。childNodes はテキストノードやコメントノードも含むすべてのノードの NodeList を返します。通常は children を使います。まとめ
最初と最後の子要素を取得する方法を整理しました。
- 推奨:
firstElementChild/lastElementChild(テキストノードを除外、シンプル) - インデックス指定:
children[0]/children[length-1](任意の位置にもアクセス可能) - CSS セレクタ:
querySelector(":first-child")(複雑な条件と組み合わせ可能) - 注意:
firstChild/lastChildはテキストノードも含む(非推奨) - null チェック: 子要素がない場合に備えてオプショナルチェーニング
?.を使う
スタイルの適用だけなら CSS の :first-child / :last-child で十分です。DOM 操作が必要な場面では firstElementChild / lastElementChild を使いましょう。