【JavaScript】querySelectorAll で取得した複数要素を操作する方法|forEach・配列変換・filter・一括スタイル変更まで解説

【JavaScript】querySelectorAll で取得した複数要素を操作する方法|forEach・配列変換・filter・一括スタイル変更まで解説 JavaScript

querySelectorAll はマッチする全要素を NodeList として返すため、取得した要素をループで一括操作する場面が頻繁にあります。一括でクラスを追加する、全要素にイベントを登録する、条件で絞り込むなど、実務で使うパターンを解説します。

この記事でわかること
・NodeList を forEach / for…of でループする方法
・スプレッド構文で配列に変換して filter / map を使う方法
・全要素に一括でクラス追加 / スタイル変更 / イベント登録
・N 番目の要素にアクセスする方法
・NodeList と HTMLCollection の違い
・DOM 変更後に NodeList が更新されない注意点
スポンサーリンク

NodeList をループで処理する

JavaScript(forEach: 最もシンプル)
const items = document.querySelectorAll(".item");

// forEach で全要素を処理
items.forEach(item => {
  item.classList.add("active");
});

// インデックスも取得可能
items.forEach((item, index) => {
  console.log(`${index}: ${item.textContent}`);
});
JavaScript(for…of)
// for...of: break / continue が使える
for (const item of document.querySelectorAll(".item")) {
  if (item.classList.contains("skip")) continue;
  item.style.color = "blue";
}
JavaScript(通常の for)
// インデックスが必要 or 逆順ループの場合
const items = document.querySelectorAll(".item");
for (let i = items.length - 1; i >= 0; i--) {
  items[i].remove(); // 逆順で削除(順方向だとインデックスがずれる)
}
ループ break continue インデックス 推奨度
forEach 不可 不可(return でスキップ) 第 2 引数 最もシンプル
for…of 可能 可能 entries() で取得可能 推奨(柔軟)
for (i) 可能 可能 変数 i 逆順ループに最適

配列に変換して filter / map / reduce を使う

JavaScript(スプレッド構文で配列変換)
const cards = document.querySelectorAll(".card");

// NodeList → 配列に変換
const cardArray = [...cards];
// または: Array.from(cards)

// filter: active クラスを持つ要素だけ抽出
const activeCards = [...cards].filter(c => c.classList.contains("active"));
console.log(activeCards.length);

// map: 各要素のテキストを配列で取得
const texts = [...cards].map(c => c.textContent.trim());
console.log(texts); // ["Card 1", "Card 2", "Card 3"]

// find: 条件に合う最初の要素
const featured = [...cards].find(c => c.dataset.featured === "true");

// some: 条件に合う要素が 1 つでもあるか
const hasError = [...cards].some(c => c.classList.contains("error"));

// reduce: 値の集計
const total = [...document.querySelectorAll(".price")]
  .reduce((sum, el) => sum + Number(el.textContent), 0);
NodeList には filter / map がない
NodeList は配列ではないため、filter / map / find / reduce を直接使えません。[...nodeList] または Array.from(nodeList) で配列に変換してから使ってください。forEachentries() だけは NodeList で直接使えます。

全要素に一括操作する実務パターン

一括クラス追加 / 削除

JavaScript
// 全 .card に active クラスを追加
document.querySelectorAll(".card").forEach(c => c.classList.add("active"));

// 全 .card から active クラスを削除
document.querySelectorAll(".card").forEach(c => c.classList.remove("active"));

// 全 .card の active を切り替え
document.querySelectorAll(".card").forEach(c => c.classList.toggle("active"));

一括スタイル変更

JavaScript
// 全 .item を非表示
document.querySelectorAll(".item").forEach(el => {
  el.style.display = "none";
});

// 特定条件の要素だけ表示
document.querySelectorAll(".item").forEach(el => {
  el.style.display = el.dataset.category === "food" ? "" : "none";
});

一括イベント登録

JavaScript
// 全ボタンにクリックイベント
document.querySelectorAll(".delete-btn").forEach(btn => {
  btn.addEventListener("click", () => {
    btn.closest(".card").remove();
  });
});

// ※ 大量の要素には「イベントデリゲーション」の方が効率的
document.querySelector(".card-list").addEventListener("click", (e) => {
  const btn = e.target.closest(".delete-btn");
  if (btn) btn.closest(".card").remove();
});

一括テキスト変更

JavaScript
// 全 .price の値を 10% 増し表示に更新
document.querySelectorAll(".price").forEach(el => {
  const original = Number(el.textContent);
  el.textContent = Math.round(original * 1.1);
});

一括属性変更

JavaScript
// 全外部リンクに target="_blank" を追加
document.querySelectorAll('a[href^="http"]').forEach(link => {
  link.setAttribute("target", "_blank");
  link.setAttribute("rel", "noopener noreferrer");
});

// 全 input を disabled にする
document.querySelectorAll("input").forEach(input => {
  input.disabled = true;
});

N 番目の要素にアクセスする

JavaScript(インデックスアクセス)
const items = document.querySelectorAll(".item");

// 最初の要素
const first = items[0];

// 最後の要素
const last = items[items.length - 1];

// 3 番目の要素(0 始まり)
const third = items[2];

// CSS の :nth-child でも取得可能
const second = document.querySelector(".item:nth-child(2)");

NodeList と HTMLCollection の違い

項目 NodeList(querySelectorAll) HTMLCollection(getElementsBy…)
更新性 静的(取得時点のスナップショット) ライブ(DOM 変更が即座に反映)
forEach 直接使える 使えない(Array.from が必要)
ループ中のクラス変更 安全(静的なので影響なし) 危険(ライブで中身が変わりスキップ発生)
返すメソッド querySelectorAll getElementsByClassName / getElementsByTagName
JavaScript(ライブコレクションの罠)
// NG: getElementsByClassName はライブ
const items = document.getElementsByClassName("old");
for (let i = 0; i < items.length; i++) {
  items[i].classList.replace("old", "new");
  // ← items からリアルタイムに消えるため、次の要素がスキップされる!
}

// OK: querySelectorAll は静的
document.querySelectorAll(".old").forEach(el => {
  el.classList.replace("old", "new"); // 安全
});
ループ中にクラスを変更するなら querySelectorAll を使う
getElementsByClassName が返す HTMLCollection は DOM の変更がリアルタイムに反映されるため、ループ中にクラスを変更すると要素がスキップされるバグが起きます。querySelectorAll の静的 NodeList ならこの問題は発生しません。

DOM 変更後の再取得の注意

JavaScript(静的 NodeList の注意点)
// querySelectorAll は取得時点のスナップショット
const items = document.querySelectorAll(".item");
console.log(items.length); // 3

// DOM に新しい要素を追加
document.querySelector(".list").innerHTML += '<div class="item">New</div>';

// NodeList は更新されない!
console.log(items.length); // まだ 3(4 ではない)

// 再取得が必要
const updatedItems = document.querySelectorAll(".item");
console.log(updatedItems.length); // 4
DOM を変更したら NodeList を再取得
querySelectorAll の NodeList は静的なので、DOM に要素を追加・削除しても自動更新されません。DOM 変更後に最新の状態が必要な場合は再度 querySelectorAll を実行してください。

実務パターン集

パターン(1): タブ切り替え

JavaScript
document.querySelectorAll(".tab-btn").forEach(btn => {
  btn.addEventListener("click", () => {
    // 全タブを非アクティブに
    document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
    document.querySelectorAll(".tab-panel").forEach(p => p.hidden = true);
    // クリックされたタブをアクティブに
    btn.classList.add("active");
    document.querySelector(btn.dataset.target).hidden = false;
  });
});

パターン(2): フィルタリング(カテゴリで絞り込み)

JavaScript
function filterProducts(category) {
  document.querySelectorAll(".product").forEach(p => {
    p.hidden = category !== "all" && p.dataset.category !== category;
  });
}

パターン(3): チェックボックスの一括チェック

JavaScript
document.querySelector("#check-all").addEventListener("change", (e) => {
  document.querySelectorAll('.item-checkbox').forEach(cb => {
    cb.checked = e.target.checked;
  });
});

パターン(4): 要素の一括削除

JavaScript
// .ad クラスの要素を全て削除
document.querySelectorAll(".ad").forEach(ad => ad.remove());

よくある質問

QquerySelectorAll の結果が 0 件のときエラーになりますか?
Aなりません。要素が見つからない場合は空の NodeList(length=0)が返ります。forEach で空の NodeList をループしても何も実行されないだけでエラーにはなりません。
QforEach 内で break したいです
ANodeList の forEach では break は使えません。途中でループを抜けたい場合は for...of + break を使うか、配列に変換して some()(return true で停止)を使ってください。
QquerySelectorAll を jQuery の $() のように使えますか?
A似た使い方ですが、jQuery は結果を jQuery オブジェクトとして返し、メソッドチェーンが可能です。querySelectorAll は素の NodeList を返すため、メソッドチェーンはできません。forEach で 1 つずつ操作する必要があります。
Q大量の要素に forEach でイベントを登録するとパフォーマンスは?
A数十個なら問題ありませんが、数百〜数千個の場合はイベントデリゲーション(親要素に 1 つだけイベントを登録)の方が効率的です。メモリ使用量が抑えられ、動的に追加された要素にも自動対応します。
QNodeList を配列に変換するのは […nodeList] と Array.from() のどちらがいいですか?
Aどちらも同じ結果です。[...nodeList] の方が短く書けるため広く使われています。Array.from(nodeList, mapFn) は変換と同時に map を適用できるメリットがあります。
QquerySelectorAll で動的に追加した要素が取得できません
AquerySelectorAll は呼び出し時点の DOM をスナップショットとして返します。要素を追加した後に再度 querySelectorAll を呼び出せば取得できます。動的要素のイベントにはイベントデリゲーションを使ってください。

まとめ

querySelectorAll で取得した複数要素の操作方法をまとめます。

やりたいこと 方法
全要素をループ nodeList.forEach(el => { … })
break / continue を使うループ for (const el of nodeList) { … }
条件で絞り込み […nodeList].filter(el => 条件)
テキストの配列を取得 […nodeList].map(el => el.textContent)
全要素にクラス追加 nodeList.forEach(el => el.classList.add(“cls”))
全要素を非表示 nodeList.forEach(el => el.hidden = true)
全要素にイベント登録 nodeList.forEach(el => el.addEventListener(…))
N 番目の要素 nodeList[n] または querySelector(“:nth-child(n)”)
最初の要素 nodeList[0]
最後の要素 nodeList[nodeList.length – 1]
全要素を削除 nodeList.forEach(el => el.remove())

querySelector / querySelectorAll の基本は「querySelector でDOM要素の取得」も併せて参照してください。