JavaScript | DOM 操作:DOM 基礎 – 静的 NodeList とライブ HTMLCollection の違い

JavaScript JavaScript
スポンサーリンク

静的 NodeList とライブ HTMLCollection とは何か

静的 NodeList とライブ HTMLCollection は、DOM から複数ノードを集めた「コレクション」を表します。違いは「DOM 変更への追従」と「含まれるノードの種類」。静的 NodeList は取得時点のスナップショットで固定(後から DOM が変わっても配列は変わらない)。ライブ HTMLCollection は常に最新の DOM に同期(要素が増減すると自動で内容が変わる)。ここが重要です:安定して扱いたいなら静的、常に最新を見たいならライブ。用途に応じて選ぶのがコツです。


どう取得するか(典型 API と返り値の違い)

静的 NodeList(固定のスナップショット)

// CSSセレクタで一致した要素の「静的な」集合
const nodes = document.querySelectorAll(".item"); // NodeList(静的)
console.log(nodes.length); // 取得時点の件数
JavaScript

querySelectorAll は静的 NodeList を返します。後で DOM を変えても、変化は反映されません。

ライブ HTMLCollection(常に最新)

// クラス名で選んだ「ライブ」な集合
const live = document.getElementsByClassName("item"); // HTMLCollection(ライブ)
console.log(live.length); // DOM変化に伴い増減
JavaScript

getElementsByClassNamegetElementsByTagNameelement.children はライブ HTMLCollection を返します。ここが重要です:子要素の追加・削除に自動追随するため、ループ中に内容が変わる可能性に注意が必要です。


何が含まれるか(要素だけか、すべてのノードか)

要素だけの集合(HTMLCollection)

HTMLCollectionは「要素ノードのみ」を含みます。空白テキストやコメントは入りません。

const ul = document.querySelector("ul");
console.log(ul.children); // <li>など要素だけ
JavaScript

NodeList は「種類次第」

NodeList は取得方法によって中身が異なります。querySelectorAll の NodeList は要素だけですが、childNodes の NodeList はテキスト・コメントも含みます(しかもライブ)。

const ul = document.querySelector("ul");
console.log(ul.childNodes); // NodeList(ライブ):テキストやコメントも含む
JavaScript

ここが重要です:要素だけを扱いたいときは children(ライブ)か querySelectorAll(静的)を選び、テキストやコメントまで扱いたいときは childNodes を使います。


DOM 変更時の挙動(静的×ライブの実例)

変更に追随しない(静的)

const nodes = document.querySelectorAll(".item"); // 2件あったとする
const list = document.querySelector("ul");

const li = document.createElement("li");
li.className = "item";
list.append(li);

console.log(nodes.length); // 2(静的なので変わらない)
JavaScript

自動で更新される(ライブ)

const live = document.getElementsByClassName("item"); // 2件
const list = document.querySelector("ul");

const li = document.createElement("li");
li.className = "item";
list.append(li);

console.log(live.length); // 3(ライブなので増える)
JavaScript

ここが重要です:ライブ集合は便利ですが、ループ中に件数が変わると予期せぬ挙動になります。反復処理やフィルタは「静的に固定した配列」に変換してから行うと安全です。


反復と配列メソッド(扱いやすさの違いを深掘り)

NodeList は forEach が使える(多くの環境で)

const nodes = document.querySelectorAll(".item");
nodes.forEach(el => el.classList.add("bind"));
JavaScript

HTMLCollection は「配列ではない」

HTMLCollection は配列メソッドを持ちません。配列に変換して扱うのが定石です。

const live = document.getElementsByClassName("item");
const arr = Array.from(live);
arr.filter(el => el.textContent.includes("JS"))
   .forEach(el => el.classList.add("hit"));
JavaScript

ここが重要です:複雑な処理(map/filter/reduce)をしたいなら、静的 NodeListを起点にするか、最初に配列へ変換してから扱うと、コードが安定して読みやすくなります。


ベストプラクティス(いつどちらを使うか)

状態を固定して安全に処理したい

  • 列挙・フィルタ・変換など「今の DOM を基準に決め打ち」したいなら querySelectorAll(静的)を使う。
  • ループ前に必ず「配列化」すると、処理中に DOM が変わっても影響を受けない。
const items = Array.from(document.querySelectorAll(".item")); // 静的→配列
JavaScript

最新の集合を常に反映したい

  • 自動で追随する必要がある UI(例:常に現在のタブ一覧を監視)ならライブを使う。
  • ただし、走査中の変動に注意し、必要なら「先に長さを保存」してから固定長ループにする。
const tabs = document.getElementsByClassName("tab");
for (let i = 0, n = tabs.length; i < n; i++) {
  // n を固定長にして、途中のDOM変化の影響を避ける
}
JavaScript

ここが重要です:大半のアプリでは「静的で扱う」ほうがバグが少ないです。ライブは「常時最新であること」に価値がある場面に限定しましょう。


典型的な落とし穴と回避策

1) ライブ集合のループ中に DOM を変更

  • 症状:長さが変わり、要素を飛ばしたり重複処理したりする。
  • 回避:事前に Array.from(collection) で固定化、または for (let i = 0, n = collection.length; i < n; i++) として長さをキャプチャ。

2) テキストノードに当たってロジックが崩れる

  • 症状:childNodes 走査で空白テキストを処理対象にしてしまう。
  • 回避:要素だけなら children(ライブ)や querySelectorAll(静的)を使う。

3) 配列メソッドを直接呼んで TypeError

  • 症状:HTMLCollection に forEach/map を呼んで失敗。
  • 回避:Array.from(...) で必ず配列化してからメソッドを使う。

ここが重要です:ライブの変動・ノード種別・配列化の3点を意識するだけで、集合まわりのトラブルはほぼ防げます。


まとめ

静的 NodeList は「取得時点のスナップショット」、ライブ HTMLCollection は「常に最新を反映」。NodeList は取得元によって要素だけのこともあれば、テキスト・コメントを含むこともあります。安定した処理には静的 NodeListや配列化が向き、常時最新が必要な監視にはライブが向きます。ループ中の変動、ノード種別、配列化を押さえれば、DOM 集合の扱いは思い通りになります。

タイトルとURLをコピーしました