はじめに
querySelector/querySelectorAllとgetElement系、どちらを使えばいいか迷うことはありませんか?この記事では、実務での最適な使い分けをまとめます。
結論
基本的には querySelector 系を使い、ID直取りだけ getElementById を使うのが現場の最適解です。
理由は「柔軟・読みやすい・一貫した挙動」の3点に集約されます。以下、詳しく見ていきましょう。
querySelector / querySelectorAll を使う理由
1. CSSセレクタがそのまま使える
複合条件・階層・疑似クラスまで一発で書けます。
// 例: #todo 内の未完了タスクのチェックボックス
container.querySelector('#todo li:not(.done) input[type="checkbox"]');
getElement*系は用途ごとに別API(getElementById / getElementsByClassName / getElementsByTagName)に分かれており、複合条件や階層指定は自前で組み合わせる必要があります。
2. どの要素を起点にも検索できる
someElement.querySelector(...) で任意のコンテナ内に限定して検索できます。コンポーネント志向のUIで局所的に探すのが簡単ですね。
3. 返り値が分かりやすい
-
querySelectorは最初の1件かnull -
querySelectorAllは静的なNodeList(後からDOMが変わっても自動更新されない)
静的なNodeListは予期せぬ副作用が起きにくいのがポイントです。対して getElementsByClassName/TagName はライブな HTMLCollection を返し、DOM変更で中身が変動してバグの温床になりがちです。
4. 読みやすく記述量が少ない
セレクタ文字列がそのまま要件を語るため、レビューや保守が楽になります。
// 読みやすい
form.querySelector('button[type="submit"]:not([disabled])');
5. 統一インターフェースで学習・移植が楽
CSSの知識がそのまま使えるので、CSS/JSの間で概念が共通化されます。テストやスクレイピング系ツール(Playwright、Puppeteerなど)ともメンタルモデルが一致するのも利点です。
getElement* を使うケース
以下の場合は getElement* 系も選択肢に入ります。
IDひとつだけを取る場合
document.getElementById('foo') は最短・高速で、IDなら意図が明確です。
巨大DOMでパフォーマンスが最優先の場合
getElementById は実装的に最速クラスです。ただし、測定して差が意味のある場面のみに限定しましょう。
ライブコレクションが意図的に必要な場合
DOM変化を自動追随させたいニッチなケースでは getElementsByClassName/TagName が有利です。
実用ガイド
迷ったら以下を基準にしてください。
- 基本は
element.querySelector(...)とelement.querySelectorAll(...)を使う - ID一本釣りなら
getElementByIdもOK(短くて速い) - コレクションが必要なら
querySelectorAllを優先。ライブ挙動が欲しいときだけgetElementsBy* - セレクタで表現できるならセレクタで書く:
[data-*]、:not()、:is()などを活用
比較コード
// ✅ 柔軟・局所検索
section.querySelector('ul.items > li.active button[aria-pressed="false"]');
// ✅ まとめて取得(静的)
const buttons = section.querySelectorAll('button.primary');
// ✅ ID一本釣り(最速)
const app = document.getElementById('app');
// ⚠️ ライブコレクション(DOM変わると中身も変わる)
const liveList = document.getElementsByClassName('item');
まとめ
通常は querySelector 系を標準装備にし、ID直取りだけ getElementById を使い分けるのが実務での最適解です。CSSセレクタの柔軟性と、静的なNodeListの安全性を活かして、保守しやすいコードを書いていきましょう。