JavaScriptのHTMLCollectionとNodeListは、HTML文書内の要素を表すために使用される配列のようなオブジェクトです。
「配列のような」というのは、どちらもlengthプロパティを持ち、インデックス番号(添字)でアクセスできるためです。
しかし実際には配列ではなく、また、HTMLCollectionとNodeListはそれぞれ似て非なる型のオブジェクトであるため、JavaScriptで扱う際には注意が必要です。
複数のHTML要素を取得する方法
JavaScriptでタグ名やクラス名などを指定して、複数の要素を取得する方法はいくつかあります。
その1 「getElementsBy」でHTML要素を取得
JavaScriptで複数のHTML要素を取得するには、下記の方法がよく使われます。
これは太古の昔からある方法です。
かつてはIE6に対応するために、これらの方法がよく使われていました。
document.getElementsByClassName()
document.getElementsByTagName()
document.getElementsByName()
ここで返されるのが HTML Collection というものです。
ブラウザの開発コンソールで以下のように試すと HTMLCollection と表示されます。
document.getElementsByTagName('div')
▶ HTMLCollection(3) [div, div, div]
以下のように配列と同様にインデックス番号(添字)で取り出すことができます。
const elms = document.getElementsByTagName('div');
console.log(elms[0]);
▶ <div>...</div>
その2 「querySelectorAll」でHTML要素を取得
複数のHTML要素を取得する別の方法として querySelectorAll
があります。
getElementsBy…よりも少し新しい方法です。といっても2008年頃からあります。
(参考) IE8 で実装された Selectors API とは何か? – IT戦記
これはCSSセレクタと同じ書き方で要素を取得できて便利です。
document.querySelectorAll('div')
▶ NodeList(3) [div, div, div]
getElementsByで取得した時と違って NodeList と表示されています。
こちらもインデックス番号(添字)でアクセスできるのは同じです。
const elms = document.querySelectorAll('div');
console.log(elms[0]);
▶ <div>...</div>
getElementsByとquerySelectorAllの使い分け
querySelectorAllの方が便利で使いやすいですが、速度面ではgetElementsByの方が速いので、特に大量に要素がある場合などは、要素の指定が単純なもの(クラス名やタグ名など)はgetElementsByで取得して、指定が複雑なものはquerySelectorAllで取得するのが良いと思います。
HTMLCollectionとNodeListの違い
では HTMLCollectionとNodeListの違いは何でしょうか?大きく2つの違いがあります。
- 要素が増減した時の挙動
- メソッドの違い
要素が増減した時の挙動
HTMLCollectionは後から要素の数が増減すると、動的に反映されます。
ライブオブジェクトと呼ばれる性質のオブジェクトです。
NodeListは要素の数が変わっても、後からそれが反映されません。
メソッドの違い
ブラウザの開発コンソールでNodeListのprototypeを展開するとわかりますが、NodeListにはforEachメソッドがありますが、HTML Collectionにはありません。
document.querySelectorAll('div')
▼ NodeList [main]
▶0: div
length: 1
▼ [[Prototype]]: NodeList
▶entries: ƒ entries()
▶forEach: ƒ forEach()
▶item: ƒ item()
▶keys: ƒ keys()
length: (...)
▶values: ƒ values()
▶constructor: ƒ NodeList()
▶Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.toStringTag): "NodeList"
▶get length: ƒ length()
▶[[Prototype]]: Object
HTMLCollectionを配列のようにループさせるには?
以下はlengthプロパティを使った昔ながらのforループです。昔からある定石のような方法です。
const elms = document.getElementsByTagName('div'); // HTMLCollection
const len = elms.length;
for (let i=0; i<len; i++) {
console.log(elms[i]);
}
ちなみにlengthをあらかじめ変数に代入しているのは、ほぼ気持ちの問題ですがループごとに要素のlengthプロパティにアクセスするよりも速そうだからです。
ES6(ECMAScript 2015)以降であれば(IE を対象外にすれば)、for of 文を使って簡潔に記述することもできます。※for inではなくfor ofであることに注意してください
const elms = document.getElementsByTagName('div'); // HTMLCollection
for (const e of elms) {
console.log(e);
}
HTML Collectionも配列に変換することで、配列と同じforEachやmapやfilterなどのメソッドが使えるようになります。
Array.from で配列に変換してmapでループ
const elms = document.getElementsByTagName('div'); // HTML Collection
const elmsAry = Array.from(elms); // 配列に変換
elmsAry.map( (v) => { console.log(v); } ); // 配列なのでmapでループできる
※Array.fromはIEは非対応です
スプレッド構文[…array]で配列に変換してmapでループ
const elms = document.getElementsByTagName('div'); // HTML Collection
const elmsAry = [...elms]; // 配列に変換
elmsAry.map( (v) => { console.log(v); } ); // 配列なのでmapでループできる
配列と似ているので、ついうっかりmapを使おうとして「ん?」となりがちなHTMLCollectionとNodeListですが、しっかり違いを理解して、楽しく要素をループさせましょう。
コメント