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