【JavaScript】最初・最後の子要素を取得する方法|firstElementChild・lastElementChild・firstChild との違い・CSS :first-child まで解説

リストの先頭アイテムにスタイルを適用する、テーブルの最終行を取得して合計を追加する、パンくずリストの最後の要素を太字にするなど、最初と最後の子要素を取得する操作は DOM 操作の基本です。JavaScript にはいくつかの取得方法があり、テキストノードの扱いに違いがあるため正しく使い分ける必要があります。

この記事でわかること
・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 の変更がリアルタイムに反映されます。firstElementChildchildren[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();
}

関連記事

よくある質問

QfirstChild と firstElementChild の違いは何ですか?
AfirstChild はテキストノード(改行・空白)やコメントノードも含むすべてのノードを対象とします。firstElementChild要素ノードのみを対象とします。通常は firstElementChild を使ってください。
Q子要素が存在しない場合はどうなりますか?
AfirstElementChild / lastElementChild は子要素がない場合 null を返します。そのまま .textContent にアクセスするとエラーになるため、if (first)el?.textContent(オプショナルチェーニング)でチェックしてください。
Q:first-child と :first-of-type の違いは?
A:first-child は「親の最初の子要素か?」を判定します。:first-of-type は「同じタグの中で最初か?」を判定します。異なるタグが混在する親要素で p:first-childp:first-of-type は異なる結果になります。
QCSS の :first-child と JavaScript の firstElementChild はどう使い分けますか?
Aスタイル変更だけなら CSS の :first-child が最適です。JavaScript は DOM 操作(テキスト変更・要素の追加/削除・属性操作)が必要な場合に使います。
Qchildren と childNodes の違いは?
Achildren要素ノードのみ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 を使いましょう。