こんにちは、さち です。
今回は、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等)はほぼ使えません。ややこしいですね……。


コメント