静的 NodeList とライブ HTMLCollection とは何か
静的 NodeList とライブ HTMLCollection は、DOM から複数ノードを集めた「コレクション」を表します。違いは「DOM 変更への追従」と「含まれるノードの種類」。静的 NodeList は取得時点のスナップショットで固定(後から DOM が変わっても配列は変わらない)。ライブ HTMLCollection は常に最新の DOM に同期(要素が増減すると自動で内容が変わる)。ここが重要です:安定して扱いたいなら静的、常に最新を見たいならライブ。用途に応じて選ぶのがコツです。
どう取得するか(典型 API と返り値の違い)
静的 NodeList(固定のスナップショット)
// CSSセレクタで一致した要素の「静的な」集合
const nodes = document.querySelectorAll(".item"); // NodeList(静的)
console.log(nodes.length); // 取得時点の件数
JavaScriptquerySelectorAll は静的 NodeList を返します。後で DOM を変えても、変化は反映されません。
ライブ HTMLCollection(常に最新)
// クラス名で選んだ「ライブ」な集合
const live = document.getElementsByClassName("item"); // HTMLCollection(ライブ)
console.log(live.length); // DOM変化に伴い増減
JavaScriptgetElementsByClassName や getElementsByTagName、element.children はライブ HTMLCollection を返します。ここが重要です:子要素の追加・削除に自動追随するため、ループ中に内容が変わる可能性に注意が必要です。
何が含まれるか(要素だけか、すべてのノードか)
要素だけの集合(HTMLCollection)
HTMLCollectionは「要素ノードのみ」を含みます。空白テキストやコメントは入りません。
const ul = document.querySelector("ul");
console.log(ul.children); // <li>など要素だけ
JavaScriptNodeList は「種類次第」
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"));
JavaScriptHTMLCollection は「配列ではない」
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 集合の扱いは思い通りになります。

