JavaScript: 「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 getel = document.getElementsByClassName('class');
var query = document.querySelectorAll('.class');

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

//console.log で種類が表示されない時はこちらで
var get_type = Object.prototype.toString.call(getel);
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>
//li要素を取得
var getel = document.getElementsByTagName('li');
var query = document.querySelectorAll('li');
//取得したli要素の数
console.log('get.length: ' + getel.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: ' + getel.length);// get.length: ???
  alert('query.length: ' + query.length);// query.length: ???
}, false);

最初に、「getElementsBy」「querySelectorAll」でそれぞれ <li> を取得。「length」で要素の数を調べると、当然どちらも「2」です。

では、ボタンをクリックして <li> を追加してから、もう一度、それぞれで要素の数を調べるとどうなるでしょう?(15, 16行目)

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

サンプル

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

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

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

つまり、常に最新の状態を取得したい要素は「getElementsBy」で一度取得するだけで OK。記述形式の違いだけでなく、この特性の差を活かして使い分けたいですね。

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

//最後の<li>要素のテキストを取得
var get_text = getel[getel.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をコピーしました