querySelector()とquerySelectorAll()は最初の1件か静的NodeListかで使い分ける
公開日: 執筆:佐藤
CSSセレクタに一致する最初の要素だけが必要ならquerySelector()、一致する全要素を処理するならquerySelectorAll()を使います。
querySelector()は要素一つ、または一致しない場合にnullを返します。querySelectorAll()は、一致した要素を文書順に持つ静的なNodeListを返します。一致しない場合もnullではなく、長さ0のNodeListです。
戻り値を一つの要素か一覧かで選ぶ
W3C Selectors API Level 1は、二つのメソッドの戻り値を次のように定義しています。
| メソッド | 一致する要素がある | 一致する要素がない |
|---|---|---|
querySelector() |
文書順で最初のElement |
null |
querySelectorAll() |
全要素を文書順に持つ静的NodeList |
長さ0の静的NodeList |
最初の通知欄だけを開く、IDで一つの入力欄を取得する、といった処理にはquerySelector()を使えます。同じclassを持つ行をすべて変更する、一覧の全リンクへ処理を加える場合はquerySelectorAll()が必要です。
「たぶん一件しかない」というHTML上の期待だけで選ばず、JavaScriptが一件だけを処理すべきか、該当する全件を処理すべきかで決めます。
querySelector()はnullを確認してから要素を操作する
架空の通知欄から最初の一件を選ぶ例は、次の形です。
var notice = document.querySelector('.notice');
if (notice !== null) {
notice.className += ' is-active';
}
これは説明用の仮例です。.noticeが一つもなければ、noticeはnullです。確認せずにclassNameやstyleへアクセスすると、その後の処理が止まる原因になります。
複数のセレクタをカンマで並べても、セレクタを書いた順の要素を返すわけではありません。document.querySelector('#foo, #bar')は、二つのどちらかに一致する要素のうち、文書内で先にある一件を返します。
優先したい対象があるなら、セレクタの並び順へ期待せず、選択条件を分けます。
querySelectorAll()はNodeListを順に処理する
同じclassを持つ全要素を処理する例は、次の形です。
var notices = document.querySelectorAll('.notice');
for (var i = 0; i < notices.length; i++) {
notices[i].className += ' is-active';
}
querySelectorAll()の戻り値はNodeListです。配列そのものとは考えず、lengthと添字、またはitem()を使って順に処理します。
一件も一致しない場合、notices.lengthは0です。上のfor文は一度も実行されません。querySelector()のようなnull確認と、querySelectorAll()の件数確認を混同しないことが重要です。
静的NodeListは取得後のDOM変更を反映しない
querySelectorAll()が返すNodeListは静的です。メソッドを呼び出した時点で一致していた要素の一覧を持ちます。そのあとDOMへ要素を追加したり、classを変更したりしても、取得済みの一覧は増減しません。
var items = document.querySelectorAll('.item');
// このあとDOMへ.itemを追加しても、itemsには自動で加わらない。
追加後の全要素を処理したい場合は、querySelectorAll()をもう一度呼び出すか、追加した要素をその場で処理します。
静的であることには、一覧を順に削除する処理で添字が途中からずれない利点があります。一方、DOMを変更しながら最新件数を監視する用途には向きません。取得する時点を処理の中で決めます。
CSSセレクタが不正なら例外になる
二つのメソッドは、class、ID、属性、子孫関係などをCSSセレクタの文字列で指定できます。たとえば、ul.nav > liなら、nav classを持つulの直下にあるliを選びます。
一致する要素がないことと、セレクタの書式が誤っていることは別です。正しいセレクタで一致しなければ、nullまたは長さ0のNodeListを返します。セレクタを解析できなければ、Selectors APIはSYNTAX_ERR例外を発生させます。
HTMLのclass名や属性値からセレクタ文字列を組み立てる場合は、完成した文字列を確認します。値を単に連結すると、空白や記号によって意図と違うセレクタになることがあります。
documentとElementで検索範囲を分ける
ページ全体から探すならdocument.querySelector()またはdocument.querySelectorAll()を使います。特定の部品内だけを探すなら、先にその親要素を取得し、親要素からメソッドを呼び出します。
var menu = document.querySelector('.global-menu');
if (menu !== null) {
var links = menu.querySelectorAll('a');
for (var i = 0; i < links.length; i++) {
// メニュー内のリンクだけを処理する。
}
}
これは説明用の仮例です。ページ全体へ同じclassが増えても、処理対象を部品内へ限定できます。セレクタを短くすること自体ではなく、どの部品を処理するかを明確にするための分離です。
まず、既存のJavaScriptで要素を取得している一か所を選んでください。一件だけを処理すべきか、全件を処理すべきかを書き、nullまたは長さ0の扱いと、DOM変更後に一覧を取り直す必要があるかを決めます。
関連記事

