JavaScriptで特定のクラスを持つ要素を一括操作するとき、getElementsByClassNameを使おうとして「forEachが使えない」と詰まった経験はありませんか?
原因はgetElementsByClassNameが返すHTMLCollectionが配列ではないからです。本記事では解決策を4つ紹介します。
getElementsByClassNameとHTMLCollectionの違い
getElementsByClassName()はHTMLCollectionを返します。HTMLCollectionは配列に似ていますが、配列ではないためforEach・map・filterなどの配列メソッドが使えません。
const items = document.getElementsByClassName("item");
console.log(items); // HTMLCollection [li.item, li.item, li.item]
console.log(Array.isArray(items)); // false
// これはエラーになる
items.forEach(el => console.log(el)); // TypeError: items.forEach is not a function
方法1:Array.from()で配列に変換する
Array.from()でHTMLCollectionを本物の配列に変換すれば、forEachなど全ての配列メソッドが使えます。
const items = document.getElementsByClassName("item");
// 配列に変換してforEach
Array.from(items).forEach(function(el) {
el.style.color = "blue";
});
// アロー関数で簡潔に
Array.from(items).forEach(el => el.classList.add("active"));
// mapやfilterも使える
const texts = Array.from(items).map(el => el.textContent);
console.log(texts); // ["テキスト1", "テキスト2", ...]
Array.from(items, el => el.textContent))。方法2:スプレッド構文で配列化する
スプレッド構文(...)を使っても配列に変換できます。モダンなJavaScriptらしい書き方です。
const items = document.getElementsByClassName("item");
[...items].forEach(el => {
console.log(el.textContent);
el.style.backgroundColor = "#f0f0f0";
});
// 配列メソッドのチェーン
[...items]
.filter(el => el.dataset.active === "true")
.map(el => el.textContent)
.forEach(text => console.log(text));
方法3:for…ofループで直接反復する
for...ofループはHTMLCollectionに対して直接使えます。配列変換が不要でシンプルです。
const items = document.getElementsByClassName("item");
for (const el of items) {
el.textContent = "更新済み";
el.setAttribute("data-processed", "true");
}
breakやcontinueで反復を制御できます。全要素を処理するだけなら最もシンプルな方法です。方法4:querySelectorAllを使う(推奨)
実はquerySelectorAll()はNodeListを返し、NodeListにはforEachが直接使えます。現代的な開発ではquerySelectorAllを使う方が便利です。
// querySelectorAllはforEachが直接使える
const items = document.querySelectorAll(".item");
items.forEach(el => {
el.style.color = "red";
});
// 複数クラスや属性の組み合わせも可能
document.querySelectorAll(".item.active[data-type='link']")
.forEach(el => console.log(el.href));
静的コレクション vs ライブコレクション
HTMLCollectionとNodeListには重要な違いがあります。
// getElementsByClassName → ライブコレクション(DOM変更を自動反映)
const liveList = document.getElementsByClassName("item");
console.log(liveList.length); // 3
document.querySelector(".item").remove();
console.log(liveList.length); // 2 ← 自動で変わる!
// querySelectorAll → 静的スナップショット
const staticList = document.querySelectorAll(".item");
console.log(staticList.length); // 3
document.querySelector(".item").remove();
console.log(staticList.length); // 3 ← 変わらない
Array.from()で配列にコピーしてからループしてください。4つの方法の比較まとめ
| 方法 | forEach使用 | 複数クラス | ライブ反映 | 推奨度 |
|---|---|---|---|---|
| Array.from(collection) | ○ | △ | × | ◎ |
| […collection] | ○ | △ | × | ◎ |
| for…of | – | △ | ○ | ○ |
| querySelectorAll | ○ | ◎ | × | ◎(最推奨) |
よくある質問(FAQ)
Array.from(nodeList)または[...nodeList]で配列に変換してください。まとめ
getElementsByClassNameはHTMLCollectionを返すためforEachが直接使えない- Array.from()またはスプレッド構文で配列化するとforEachが使える
- for…ofはHTMLCollectionに直接使えてシンプル
- 現代的な書き方ではquerySelectorAllを使うのが最もスマート
- ループ中にDOMを変更する場合はライブコレクションに注意