スポンサーリンク

JS:「querySelectorAll」は「getElementsBy」の簡略版ではない

この記事は約7分で読めます。

イメージ

こんにちは、さち です。

今回は、JavaScript の備忘録です。

サイト上の要素を取得する時は「querySelectorAll」を使うと
jQuery と似た CSS のような形式で記述ができて超便利です。

『これなら「getElementsBy」は要らないんじゃ?』
と思うんですが、そうはいきません。
「getElementsBy」と「querySelectorAll」では動作に違いがあるのです。

スポンサーリンク

「querySelectorAll」は CSS っぽい動作で簡単&便利

例えば、下記のような HTML があります。
「two」クラスを持つ <div> の中にある <li> をすべて取得してみます(11~14行目)。

<div class="one">
  <ul>
    <li>1-1</li>
    <li>1-2</li>
    …
    <li>1-X</li>
  </ul>
</div>
<div class="two">
  <ul>
    <li>2-1</li>
    <li>2-2</li>
    …
    <li>2-X</li>
  </ul>
</div>

「getElementsBy」と「querySelectorAll」、それぞれで取得するとこんな感じ。

//「getElementsBy」を使って取得
var elm = document.getElementsByClassName('two')[0];
var li = elm.getElementsByTagName('li');

//「querySelectorAll」を使って取得
var li = document.querySelectorAll('.two li');

「querySelectorAll」は、CSS のような記述で
直感的かつ簡単に要素を取得できるのがメリット。
CSS を書ける人にとっては神がかった機能です。

『じゃあ、常に「querySelectorAll」使えばよくない?』と思うんですが
残念ながら、それは無理です。
両者では取得できるものが違うためです。

「getElementsBy」と「querySelectorAll」の違い

適当なクラスを「getElementsBy」と「querySelectorAll」それぞれで取得。
取得した要素を調べてみます。

var get = document.getElementsByClassName('class');
var query = document.querySelectorAll('.class');

console.log(get);// HTMLCollection []
console.log(query);// NodeList []

//console.log で種類が表示されない時はこちらで
var get_type = Object.prototype.toString.call(get);
var query_type = Object.prototype.toString.call(query);
console.log(get_type);// [object HTMLCollection]
console.log(query_type);// [object NodeList]

「getElementsBy」は HTMLCollection(HTMLコレクション) を取得して
「querySelectorAll」は NodeList(ノードリスト) を取得しています。

どちらも同じ要素を取得したはずなのに、受け取ったものの種類が異なります。
これが、「getElementsBy」と「querySelectorAll」の決定的な違いです。

何か挙動が違うっぽいことは分かりました。
でも、HTMLCollection と NodeList では一体何が違うの……?

「HTMLCollection」と「NodeList」の違い

例として、下記のようなページを作ってみました。
ボタンをクリックすると、リストに新しい <li> 要素を追加します。

<ul>
  <li>かん</li>
  <li>かん</li>
</ul>
<button>「みかん」を追加</button>

最初に、「getElementsBy」「querySelectorAll」でそれぞれ <li> を取得。
「length」で要素の数を調べると、当然どちらも「2」です。
では、ボタンをクリックして <li> を追加してから
もう一度、それぞれで要素の数を調べるとどうなるでしょう?(15, 16行目)

//li要素を取得
var get = document.getElementsByTagName('li');
var query = document.querySelectorAll('li');
//取得したli要素の数
console.log('get.length: ' + get.length);// get.length: 2
console.log('query.length: ' + query.length);// query.length: 2

//ボタンをクリックしたらリストに「みかん」を追加
var ul = document.getElementsByTagName('ul')[0];
var button = document.getElementsByTagName('button')[0];
button.addEventListener('click', function() {
  var li = document.createElement('li');
  li.textContent = 'みかん';
  ul.appendChild(li);
  alert('get.length: ' + get.length);// get.length: ???
  alert('query.length: ' + query.length);// query.length: ???
}, false);

実際に、試してみて下さい。

サンプル

「get.length」は、追加した <li> の分だけ増えていくのに対して
「query.length」は、ずっと「2」のままですね。

「getElementsBy」で取得した
「HTMLCollection」はリアルタイムで更新されるのに対して
「querySelectorAll」で取得した
「NodeList」は取得時点の状態のままということ。
操作画面

この特性から
HTMLCollection は、動的な要素(=生きてる)
NodeList は、静的な要素(=死んでる) などと言われます。

つまり、常に最新の状態を取得したい要素は
「getElementsBy」で一度取得するだけで OK です。

記述形式の違いだけでなく、この特性の差を活かして使い分けたいですね。

ちなみに、ボタンを押した後に
get と query から最後尾の <li>要素 のテキストを取得すると
get の方は、ちゃんと追加した「みかん」が出力されます。

//最後の<li>要素のテキストを取得
var get_text = get[get.length - 1].textContent;
var query_text = query[query.length - 1].textContent;

console.log(get_text);// みかん
console.log(query_text);// かん

 

まとめ

それぞれのメソッドで何が返ってくるのか表にまとめました。
表

「getElementsBy」で取得すると
HTMLCollection になるという感じで書いてきましたが
「getElementsByName」は NodeList です。
気をつけましょう。

ちなみに、「HTMLCollection」「NodeList」はどちらも配列に見えますが
あくまで「配列のようなもの」であって、純粋な配列ではありません。
そのため、配列のプロパティ,メソッド(forEach等)はほぼ使えません。
ややこしいですね……。

コメント

タイトルとURLをコピーしました