【JavaScript】子要素を取得・操作する方法|children・childNodes・firstElementChild・追加・削除・置換まで解説

【JavaScript】子要素を取得・操作する方法|children・childNodes・firstElementChild・追加・削除・置換まで解説 JavaScript

JavaScript で DOM を操作する際、親子関係のナビゲーションは基本中の基本です。子要素の一覧を取得する、最初/最後の子を取得する、子要素を追加・削除・置換する――これらを children / childNodes / append / remove 等のプロパティ・メソッドで行います。

この記事でわかること
・children(要素ノードのみ)と childNodes(テキストノード含む)の違い
・firstElementChild / lastElementChild / children[n] での個別アクセス
・parentElement / closest で親要素を辿る方法
・append / prepend / before / after / insertBefore で子要素を追加
・remove / removeChild で子要素を削除
・replaceWith / replaceChild で子要素を置換
・innerHTML との違いと使い分け
スポンサーリンク

children と childNodes の違い

HTML(サンプル)
<ul id="list">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
JavaScript(children vs childNodes)
const list = document.getElementById("list");

// children: 要素ノードのみ(li だけ)
console.log(list.children.length);    // 3
console.log(list.children[0].tagName); // "LI"

// childNodes: テキストノード(改行・空白)も含む
console.log(list.childNodes.length);   // 7(テキスト + li + テキスト + li + ...)
console.log(list.childNodes[0].nodeType); // 3(Text ノード = 改行・空白)
プロパティ 返す内容 戻り値 推奨
children 要素ノードのみ(HTML タグ) HTMLCollection(ライブ) 推奨(ほとんどの場合これで十分)
childNodes 全ノード(要素 + テキスト + コメント) NodeList(ライブ) テキストノードも必要な場合のみ
通常は children を使う
HTML のインデント(改行・スペース)はテキストノードとして DOM に含まれます。childNodes だとこれらのテキストノードもカウントされるため、意図しない結果になりがちです。要素だけを扱いたい場合はchildren を使ってください

子要素・兄弟要素・親要素のナビゲーション

プロパティ 取得対象 要素のみ
children 全子要素 はい
firstElementChild 最初の子要素 はい
lastElementChild 最後の子要素 はい
children.length / childElementCount 子要素の数 はい
parentElement 親要素 はい
nextElementSibling 次の兄弟要素 はい
previousElementSibling 前の兄弟要素 はい
closest(“selector”) 最も近い祖先要素 はい(CSS セレクタ指定)
JavaScript(ナビゲーションの例)
const list = document.getElementById("list");

// 最初の子要素
console.log(list.firstElementChild.textContent); // "Item 1"

// 最後の子要素
console.log(list.lastElementChild.textContent);  // "Item 3"

// N 番目の子要素(0 始まり)
console.log(list.children[1].textContent);        // "Item 2"

// 子要素の数
console.log(list.children.length);                 // 3
console.log(list.childElementCount);                // 3(同じ結果)

// 親要素
const item = list.children[0];
console.log(item.parentElement.id);                 // "list"

// 兄弟要素
console.log(item.nextElementSibling.textContent);   // "Item 2"
console.log(list.children[1].previousElementSibling.textContent); // "Item 1"
firstChild / lastChild / nextSibling はテキストノードも返す
firstChild / lastChild / nextSibling / previousSibling はテキストノード(改行等)も返すため、意図しない結果になりがちです。Element 付きのプロパティfirstElementChild 等)を使ってください。

子要素を追加する

append / prepend(モダン: 推奨)

JavaScript(append / prepend)
const list = document.getElementById("list");

// 末尾に追加
const newItem = document.createElement("li");
newItem.textContent = "Item 4";
list.append(newItem);

// 先頭に追加
const firstItem = document.createElement("li");
firstItem.textContent = "Item 0";
list.prepend(firstItem);

// 文字列も直接追加可能
list.append("テキストノード"); // テキストノードとして追加

// 複数要素を同時に追加
const li1 = document.createElement("li");
li1.textContent = "A";
const li2 = document.createElement("li");
li2.textContent = "B";
list.append(li1, li2); // 2 つ同時に末尾に追加

before / after(兄弟として追加)

JavaScript(before / after)
const secondItem = list.children[1];

// secondItem の前に挿入
const newBefore = document.createElement("li");
newBefore.textContent = "Before 2";
secondItem.before(newBefore);

// secondItem の後に挿入
const newAfter = document.createElement("li");
newAfter.textContent = "After 2";
secondItem.after(newAfter);

insertBefore(従来の方法)

JavaScript(insertBefore)
// 指定した子要素の前に挿入(従来の API)
const newItem = document.createElement("li");
newItem.textContent = "Inserted";
list.insertBefore(newItem, list.children[1]); // 2 番目の前に挿入

// 第 2 引数が null なら末尾に追加(= appendChild と同じ)
list.insertBefore(newItem, null);
メソッド 挿入位置 文字列対応 複数要素 推奨
append() 末尾 はい はい 推奨
prepend() 先頭 はい はい 推奨
before() 要素の前(兄弟) はい はい 推奨
after() 要素の後(兄弟) はい はい 推奨
appendChild() 末尾 いいえ いいえ 従来(append で代替可)
insertBefore() 指定要素の前 いいえ いいえ 従来(before で代替可)
append / prepend / before / after がモダンで推奨
appendChild / insertBefore は従来の API で、文字列や複数要素の同時追加ができません。ES2017 以降の append / prepend / before / after はより柔軟で推奨です。

子要素を削除する

JavaScript(remove / removeChild)
// remove(): 要素自身を削除(推奨)
list.children[1].remove(); // 2 番目の子要素を削除

// removeChild(): 親から子を削除(従来)
list.removeChild(list.children[0]); // 最初の子要素を削除

// 全子要素を削除
while (list.firstChild) {
  list.firstChild.remove();
}

// innerHTML で全削除(最もシンプルだが注意点あり)
list.innerHTML = "";

// replaceChildren() で全削除(推奨: イベントリスナーもクリーンに解除)
list.replaceChildren();
方法 推奨 備考
el.remove() 推奨 要素自身を削除。最もシンプル
parent.removeChild(child) 従来 親要素の参照が必要
parent.innerHTML = “” 手軽 イベントリスナーが残る可能性。XSS リスク
parent.replaceChildren() 推奨 全子要素を安全に削除。引数で置換も可能

子要素を置換する

JavaScript(replaceWith / replaceChild)
// replaceWith(): 要素自身を別の要素に置換(推奨)
const oldItem = list.children[0];
const newItem = document.createElement("li");
newItem.textContent = "Replaced!";
oldItem.replaceWith(newItem);

// replaceChild(): 親が子を置換(従来)
list.replaceChild(newItem, list.children[0]);

// replaceChildren(): 全子要素を置換
const li1 = document.createElement("li");
li1.textContent = "New 1";
const li2 = document.createElement("li");
li2.textContent = "New 2";
list.replaceChildren(li1, li2); // 既存の子を全削除して li1, li2 に置換

createElement で要素を作成する

JavaScript(要素の作成パターン)
// 基本: createElement + プロパティ設定 + append
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
  <h3>${title}</h3>
  <p>${description}</p>
`;
document.querySelector(".container").append(card);

// template リテラルで HTML を直接挿入(簡潔だが XSS に注意)
document.querySelector(".container").insertAdjacentHTML(
  "beforeend",
  `<div class="card"><h3>${title}</h3><p>${description}</p></div>`
);
方法 安全性 簡潔さ
createElement + append 安全(textContent で XSS 防止) 冗長
innerHTML XSS リスク(ユーザー入力に注意) 簡潔
insertAdjacentHTML XSS リスク(同上) 簡潔 + 既存要素を壊さない
template 要素 安全 やや冗長
innerHTML はイベントリスナーをリセットする
el.innerHTML += "..." は既存の子要素を全て再パースするため、子要素に登録していたイベントリスナーが消えます。要素を追加するだけなら append()insertAdjacentHTML("beforeend", ...) を使ってください。

実務パターン集

パターン(1): 配列からリスト要素を生成

JavaScript
const fruits = ["Apple", "Banana", "Cherry"];
const ul = document.querySelector("#fruit-list");

fruits.forEach(fruit => {
  const li = document.createElement("li");
  li.textContent = fruit;
  ul.append(li);
});

パターン(2): 子要素を全てループして処理

JavaScript
const container = document.querySelector(".container");

// children で全子要素をループ
for (const child of container.children) {
  child.classList.add("processed");
}

// 配列に変換して filter
const divChildren = [...container.children].filter(c => c.tagName === "DIV");

パターン(3): 子要素の並び替え

JavaScript
// テキスト順で並べ替え
const list = document.querySelector("#list");
const sorted = [...list.children].sort((a, b) =>
  a.textContent.localeCompare(b.textContent)
);
list.replaceChildren(...sorted);

パターン(4): 空の親要素をチェック

JavaScript
// 子要素がなければメッセージを表示
const list = document.querySelector("#todo-list");
if (list.children.length === 0) {
  const msg = document.createElement("p");
  msg.textContent = "タスクがありません";
  msg.className = "empty-message";
  list.append(msg);
}

パターン(5): 削除ボタン付きリストアイテム

JavaScript
function addTodo(text) {
  const li = document.createElement("li");
  li.textContent = text;

  const deleteBtn = document.createElement("button");
  deleteBtn.textContent = "削除";
  deleteBtn.addEventListener("click", () => li.remove());

  li.append(deleteBtn);
  document.querySelector("#todo-list").append(li);
}

よくある質問

Qchildren と childNodes はどちらを使うべきですか?
Aほとんどの場合children を使ってください。children は HTML 要素(タグ)だけを返します。childNodes は改行やスペースのテキストノードも含むため、意図しないカウントや操作の原因になります。テキストノードを操作したい場合のみ childNodes を使います。
Qappend と appendChild の違いは?
Aappend は文字列の直接追加と複数要素の同時追加が可能で、戻り値はありません。appendChild は要素のみ(1 つずつ)で、追加した要素を戻り値として返します。新規コードでは append を推奨します。
QinnerHTML = “” で子要素を全削除しても大丈夫ですか?
A動作はしますが、子要素に登録していたイベントリスナーが残り続ける可能性があります(メモリリーク)。replaceChildren()(引数なし)の方が安全に全削除できます。
QfirstChild がテキストノードを返すのはなぜですか?
AHTML のインデント(改行 + スペース)はテキストノードとして DOM に含まれるためです。firstElementChild を使えば、テキストノードをスキップして最初の要素ノードだけを取得できます。
Qchildren は配列ですか?
Aいいえ。childrenHTMLCollection(ライブ)です。forEach は直接使えないため、[...el.children].forEach(...) で配列に変換してから使うか、for...of を使ってください。
Q子要素の順番を入れ替えるには?
A[...parent.children] で配列に変換 → sort() で並べ替え → parent.replaceChildren(...sorted) で全置換するのが最もシンプルです。insertBefore で 1 つずつ移動する方法もありますが冗長です。

まとめ

子要素の取得・操作方法をまとめます。

やりたいこと 推奨方法
全子要素を取得 el.children(要素のみ)
最初 / 最後の子要素 firstElementChild / lastElementChild
N 番目の子要素 el.children[n]
子要素の数 el.children.length / childElementCount
親要素を取得 el.parentElement
末尾に追加 parent.append(child)
先頭に追加 parent.prepend(child)
特定要素の前 / 後に追加 el.before(new) / el.after(new)
要素を削除 el.remove()
全子要素を削除 parent.replaceChildren()
要素を置換 el.replaceWith(newEl)
子要素の並べ替え […children].sort() + replaceChildren(…sorted)

querySelector での要素取得は「querySelector / querySelectorAll の使い方」、classList でのクラス操作は「classList の使い方完全ガイド」も併せて参照してください。