querySelectorAll の基本 — document.querySelectorAll('li')
querySelectorAll は「CSSセレクタに一致する要素を“全部”まとめて取得」するメソッドです。返り値は配列のように見える NodeList(静的)で、見つからない場合は空のリストになります。
基本の使い方
<ul id="list">
<li class="item">A</li>
<li class="item">B</li>
<li class="item">C</li>
</ul>
<script>
const items = document.querySelectorAll("#list .item"); // すべての .item
console.log(items.length); // 3
console.log(items[0].textContent); // "A"
</script>
HTML- 対象: CSSセレクタで一致する“全て”の要素。
- 型:
NodeList(静的)。lengthや添字アクセス、forEachが使える。 - 空のとき: 例外ではなく、
length === 0の空リスト。
よくあるセレクタ例
document.querySelectorAll(".card"); // クラス
document.querySelectorAll("#menu li"); // 入れ子
document.querySelectorAll("ul > li.item"); // 直接の子(>)
document.querySelectorAll("[data-id]"); // 属性があるもの
document.querySelectorAll("input[type='text']");
JavaScript- 範囲を絞る: 親要素からの相対検索も使える(後述)。
まとめて処理するテンプレート集
全要素にクラスを付ける・外す
const boxes = document.querySelectorAll(".box");
boxes.forEach(el => el.classList.add("active"));
JavaScriptテキストを一括で整形
const lis = document.querySelectorAll("#list li");
lis.forEach((li, i) => {
li.textContent = `${i + 1}. ${li.textContent.trim()}`;
});
JavaScriptクリックイベントを全てに付ける
const buttons = document.querySelectorAll(".like");
buttons.forEach(btn => {
btn.addEventListener("click", () => {
console.log("liked:", btn.closest(".card")?.dataset.id);
});
});
JavaScript複雑なら親から相対検索(:scope)
const card = document.querySelector(".card.selected");
const inside = card.querySelectorAll(":scope .title, :scope .desc"); // 親の中だけ
JavaScriptNodeList と配列の違いと変換
- そのまま使えるもの:
length, 添字items[i],forEach(モダンブラウザ)。 - 足りないもの:
map,filter,reduceはない。使いたい場合は配列へ変換。
const nodes = document.querySelectorAll(".item");
const arr = Array.from(nodes); // または [...nodes]
const labels = arr.map(el => el.textContent);
JavaScript- 静的であること: DOMが後から変わっても、取得済みの
NodeListは自動更新されません。必要なら「もう一度querySelectorAllで取り直す」か、イベント委譲を使う。
実務でのコツ
- 範囲を狭くして安全に:
document全体ではなく、親要素からのquerySelectorAllで「ここだけ」を対象に。
const form = document.querySelector("#profile");
const inputs = form.querySelectorAll("[name]");
JavaScript- 存在チェックは length: 例外にならないので、
if (nodes.length === 0) { ... }のように分岐。 - 動的追加に強い設計: 後から要素が増えるなら、都度取り直すか、親でクリック委譲を使う。
document.getElementById("list").addEventListener("click", (e) => {
const li = e.target.closest("li");
if (!li) return;
console.log("clicked:", li.textContent);
});
JavaScript- パフォーマンス: 重いセレクタを何度も呼ぶと遅い。親をキャッシュし、必要な最小範囲で検索する。
ありがちなハマりポイントと対策
- map/filter が使えない:
- 対策:
Array.from(nodes)や[...]で配列化してから使う。
- 対策:
- 取得後に増えた要素が対象外(静的):
- 対策: 再取得するか、イベント委譲で動的に対応。
- スコープが広すぎて別要素に当たる:
- 対策: 親からの相対検索&短い読みやすいセレクタにする。
- 疑似クラスの誤解:
:hoverなどは「実際の状態」に依存。選択条件は現実のレンダリングに左右される点に注意。
練習問題(手を動かして覚える)
<ul id="list">
<li class="item">Apple</li>
<li class="item">Banana</li>
<li class="item">Cherry</li>
</ul>
<button id="add">Add</button>
<script>
const list = document.getElementById("list");
const add = document.getElementById("add");
// 1) すべての li に連番を付ける
document.querySelectorAll("#list li").forEach((li, i) => {
li.textContent = `${i + 1}. ${li.textContent}`;
});
// 2) ボタンで項目を追加 → 再取得して処理
add.addEventListener("click", () => {
const li = document.createElement("li");
li.className = "item";
li.textContent = "New";
list.appendChild(li);
// 追加後に再取得(静的NodeListなので)
document.querySelectorAll("#list li").forEach((li, i) => {
li.dataset.index = String(i + 1);
});
});
// 3) クリック委譲でどの項目でも反応
list.addEventListener("click", (e) => {
const li = e.target.closest("li.item");
if (!li) return;
console.log("clicked index:", li.dataset.index);
});
</script>
HTML直感的な指針
- 「CSSセレクタで全部取る」→
NodeListをforEachで処理、配列メソッドが欲しければArray.from。 - 範囲は親から絞り、動的に増えるなら再取得かイベント委譲で。
- セレクタは短く、読みやすく。
