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(テキストノード含む)の違い
・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 に含まれます。
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 以降の
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 の違いは?
A
append は文字列の直接追加と複数要素の同時追加が可能で、戻り値はありません。appendChild は要素のみ(1 つずつ)で、追加した要素を戻り値として返します。新規コードでは append を推奨します。QinnerHTML = “” で子要素を全削除しても大丈夫ですか?
A動作はしますが、子要素に登録していたイベントリスナーが残り続ける可能性があります(メモリリーク)。
replaceChildren()(引数なし)の方が安全に全削除できます。QfirstChild がテキストノードを返すのはなぜですか?
AHTML のインデント(改行 + スペース)はテキストノードとして DOM に含まれるためです。
firstElementChild を使えば、テキストノードをスキップして最初の要素ノードだけを取得できます。Qchildren は配列ですか?
Aいいえ。
children は HTMLCollection(ライブ)です。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 の使い方完全ガイド」も併せて参照してください。

