JavaScriptのセレクタの取得方法を考えてみる

/

導入

Webページを設計する際にJavaScriptで良く行う処理の一つにHTMLタグに付けたIDやClassを目印にDOMの操作を行うというものがあります。
その際目的のノードを取得はgetElementByIdなどを使って行っていましが、比較的新しく追加されたquerySelectorを使っても同じような事が出来ると聞いたのでそれぞれの違いを調べてみました。

HTML

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="styles/style.css">
    <script src="scripts/main.js"></script>
  </head>
  <body>
    <div id="target">ID target</div>
    <div class="targets">class targets1</div>
    <div class="targets">
      <h2>class targets2</h2>
    </div>
    <div class="targets">class targets3</div>
  </body>
</html>

getElement

document.getElementById

document.getElementByIdは指定したIDを持つ要素の値を返します。

var target_id = document.getElementById("target");
console.log(target_id);
<div id="target">ID target</div>

document.getElementById/MDN

document.getElementsByClassName

document.getElementsByClassNameは指定したクラス名を持つ要素の集合をリスト形式で返します。

var targets_class = document.getElementsByClassName("targets");
console.log(targets_class);
[div.targets,div.targets,div.targets]

document.getElementsByClassName/MDN

指定したクラスリスト内の個々の要素を取得する

getElementsByClassNameやquerySelectorAllのようにマッチする複数の要素をリスト形式で返す関数の場合はforループなどを使うとリスト内の個々の要素を取りだす事が出来ます。

for(var i = 0; i < targets_class.length; i++){
  console.log(targets_class[i]);
}
<div class="targets">class targets1</div>
<div class="targets">
  <h2>class targets2</h2>
</div>
<div class="targets">class targets3</div>

指定したクラスのうち更に特定の要素だけでイベントを起こす

取得した個々の要素にaddEventListenerなどで関数を追加すれば、指定した要素にだけイベント発生させる事が出来ます。

for(var i = 0; i < targets_class.length; i++){
  targets_class[i].addEventListener("click", function(event){
    console.log(event.target);
  });
}
#クリックされた要素が表示される
<div class="targets">class targets3</div>

querySelector

document.querySelector

querySelectorは与えられたCSSセレクタにマッチする最初の要素の値を返します。

var target_id = document.querySelector("#target");
console.log(target_id);
<div id="target">ID target</div>

querySelectorは一つしか値を返しませんがgetElementByIdと違いID以外のセレクタを指定する事も出来るので、セレクタにIDを指定するときには「#target」のように#まで含める必要があります。

document.querySelector/MDN

document.querySelectorAll

querySelectorAllは指定したCSSセレクタを持つ全ての要素をリスト形式で返します。

var targets_class = document.querySelectorAll(".targets");
console.log(targets_class);
[div.targets,div.targets,div.targets]

querySelector同様にquerySelectorAllもクラス以外の要素も指定できるため「.targets」のようにクラス名を指定したい場合にはクラスである事明示する必要があります。

document.querySelectorAll/MDN

querySelectorで指定出来る要素

querySelectorは取り出す要素の指定をCSSセレクタの形式で行うためIDやClass以外にもaタグなどのHTMLタグを指定する事が可能で、またこれらの要素を組み合わせる事も出来ます。

var target = = document.querySelectorAll(".targets > h2");
console.log(target[0]);
<h2>class targets2</h2>

擬似クラスによる指定も可能。

<ul>
  <li>リスト1</li>
  <li>リスト2</li>
  <li>リスト3</li>
</ul>
var target = = document.querySelectorAll("ul li:nth-child(2)");
console.log(target[0]);
<li>リスト2</li>

querySelectorの特徴

当初漠然とClassやIDで指定した要素を取得する別の手段ぐらいに思っていましたが、調べてみるとどうやらquerySelectorはJavaScriptでDOMの要素をCSSのように取得するための関数のようです。

上で見たとおりquerySelectorは一つの関数でClassやHTMLタグをまとめて指定することが可能なため、CSSを適用する際にセレクタを指定するのと同じような形で目的のノードが取得できます。

var target = document.querySelector(
  " header > .header_inner > nav > ul > li > a "
);
header > .header_innner > nav > ul > li > a {
  color: red;
}

確かにこれは便利です。

getElementにもgetElementByTagNameというHTMLタグ要素を取得する関数がありますし、またchildNodeなどを使えば指定した要素の子要素を取得できるため同じ様にセレクタを指定する事は自体は可能です。
しかしその場合取得する要素毎にgetElementの関数の切り替えや、下の階層の要素を取得しようと思えばその都度chidlNodeを呼び出す必要があります。
もっと簡単に書ける方法もあるのかもしれませんが一つの関数の引数にまとめてセレクタを渡せるquerySelectorのほうがより簡単であるというのは間違いないと思います。

Webページをデザインする際に実際上のような形でCSSを指定する事はザラなのでJavaScriptをデザイン用途に使う場合はquerySelectorを使ったほうが楽にコードを書くことが出来そうです。
コードの量自体が減らせますし、プログラミングに慣れていないデザイナー寄りのフロントエンドエンジニアが扱う場合にも「CSSと同じようにセレクタを指定できる」というのは直感的に理解しやすいのではないかと思います。

処理速度

一方でquerySelectorのほうが複雑な処理を行えるせいかquerySelecotrよりもgetElementを使った方が動作は高速になるようです。

【訳注: 上記ではクラスセレクタを用いた検索を例示していますが、この様な単発のクラス名の場合は getElementsByClassName() メソッドを用いた方が高速な動作となります。また、ID セレクタ ( # ) を対象とする検索も可能ですが、その場合は getElementById() メソッドを用いた方が高速です。対象セレクタが流動的で有る場合や、または隣接セレクタなどによるコンビネーションセレクタでの複雑な検索の場合に於いて、querySelector() や querySelectorAll() は真価を発揮します。】
document.querySelector/MDN

条件付きの説明がなされていますが基本的にはgetElementByIdなどを使ったほうが処理速度は速いようなので、場合によってはgetElementを使ったり両者を上手く組み合わせる必要があるようです。

ただしquerySelectorを使えばjQueryなどのライブラリを使わずに済むケースもあると思います。その場合よりサイトを軽く出来る可能性があるため、そういった事も含めてどちらを使用するのか考える必要がありそうです。

あとがき

調べてみたら当初思っていたものと違ったものだったのでエントリはなんだかグダグダになってしまいました。
ただquerySelectorの機能自体はシンプルかつ有用なので上手く使えばDOM地獄から抜け出すのに役立ってくれそうです。