HTMLCollectionとNodeListについて調べると、下記のような内容が書いてあります。
・どちらも配列風オブジェクト
・HTMLCollectionは動的、NodeListは静的
・NodeListではforEachが使えるが、HTMLCollectionでは使えない
初心者の私には「なんだなんだ??」「配列風?風ってなんだ?」となります笑
「このトピック、ちょっとトリッキー(理解するのにコツがいる)な箇所なんだよね」とDevEdも動画の中で言っていました。
今わかっている範囲内で、順にまとめてみようと思います。
配列風と言われている理由
HTMLCollectionとNodeList、どちらもHTML文書内の要素(HTML要素)を収集またはグループ化するためのJavaScriptオブジェクトです。
配列風と言われている理由は、どちらもlengthプロパティを持ち、インデックス番号(添字)でアクセスできるためです。
いつ使うのか?
どちらも、HTML要素へアクセスして複数の要素を取得する際に使います。
ここまでをまとめると、JavaScriptでタグ名やクラス名などを指定して、複数の要素を取得する方法があり、一つはHTMLCollection、もう一つはNodeListということですね。
HTMLCollectionとは?
HTMLCollectionでは、document.getElementsByTagName()
, document.getElementsByClassName()
, document.getElementsByName()
などのメソッドを使用して、複数のHTML要素を取得します。
document.getElementsByClassName()
document.getElementsByTagName()
document.getElementsByName()
ここからは例です。
こんな感じで、シンプルな「todo-list」を書いたとします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello</h1>
<button>click me</button>
<ul id="todo-list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
</ul>
<h2 class="todo-num"></h2>
<script src="dom.js"></script>
</body>
</html>
「todo-list」の要素を取得するために、document.getElementsByClassName()
を使ってみると
const items = document.getElementsByClassName('item');
console.log(items);
コンソールにはHTMLCollectionが返ってきました。
また、中の要素を取り出すには、インデックス(添字)でアクセスできます。
const items = document.getElementsByClassName('item');
console.log(items[0]);
こちらを展開すると(左の▶︎をクリック)下の方の「textContent」に「Item 1」というのがあり、リストの一つ目が取得されたことがわかります。
NodeListとは?
NodeListでは、querySelectorAllを使って複数のHTML要素を取得します。
「todo-list」の要素を取得するために、querySelectorAllを使ってみましょう。
const items = document.querySelectorAll('li');
console.log(items);
コンソールにはNodeListが返ってきました。
こちらもインデックス(添字)でアクセスできます。
const items = document.querySelectorAll('li');
console.log(items[0]);
HTMLCollectionとNodeListの違い
さて、ここからが本題のHTMLCollectionとNodeListの違いについてです。
【違い①】保持できるデータの種類
・HTMLCollection:HTMLの要素(element)のみを保持できる
・NodeList:HTMLの要素(element)に加えて、テキストやコメントも保持できる
※HTMLの要素(element)とは?:Webページを構成する要素のこと。代表的なものとして、<html>, <head>, <title>, <body>, <h1>
, <h2>
, <h3>
, … <h6>
, <p>, <a>, <img>, <ul>, <ol>がある。
【違い②】使えるメソッドが違う
・HTMLCollection:forEachメソッド使えない
・NodeList:forEachメソッドが使える
※forEachメソッドとは?:配列に対してだけ使えるメソッドで、配列の各要素に対して繰り返し同じ処理をしてくれる。
上のHTMLのコードはそのままに、HTMLCollectionでforEachメソッドを使ってみるとエラーが出ました。
const htmlCollection = document.getElementsByClassName('item');
const nodeList = document.querySelectorAll('.item');
htmlCollection.forEach(function (item) {
console.log(item);
});
次にNodeListでforEachを使ったところ、中の要素を取得することができました。
const htmlCollection = document.getElementsByClassName('item');
const nodeList = document.querySelectorAll('.item');
nodeList.forEach(function (item) {
console.log(item);
});
forEachが配列だけに使えるメソットであるということは、nodeListの方がより配列っぽい構造になっているということができるのかなと思いました。
【違い③】要素が増減した時に、自動的に反映されるかどうか
・HTMLCollection:要素が増減した際に、自動的に反映される(動的)
・NodeList:要素が増減した際に、自動的に反映されない(静的)
違いの3つ目としては、要素が増減した際の動きがあります。HTMLCollectionは要素が増減した際に、自動的に反映されるので動的、NodeListは要素が増減した際に、自動的に反映されないので静的と言われています。
JavaScriptを使って、こちらの(これまで使ってきたHTMLのコードと同じです)todo-listの中にliを一つ増やして、「Item 3」を加えるコードを書いてみます。
HTMLCollectionの場合
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello</h1>
<button>click me</button>
<ul id="todo-list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
</ul>
<h2 class="todo-num"></h2>
<script src="dom.js"></script>
</body>
</html>
const htmlCollection = document.getElementsByClassName('item');
const todoList = document.getElementById('todo-list');
//最初の状態: itemは2つ
console.log(htmlCollection.length); //-> 2
//新しいliを作成する
const newItem = document.createElement('li');
//作成したliにクラス名をつける
newItem.classList.add('item');
//テキストを追加する
newItem.innerText = 'Item 3';
//ulのtodo-listに新しく作成したリストを追加する
todoList.appendChild(newItem);
//追加された要素が反映されている
console.log(htmlCollection);
console.log(htmlCollection.length); //-> 3
結果はこのように、Item 3が加えられ、追加された要素が反映されていることがわかります。
NodeListの場合
const nodeList = document.querySelectorAll('.item');
const todoList = document.querySelector('#todo-list');
//最初の状態: itemは2つ
console.log(nodeList.length); //-> 2
//新しいリストを作成する
const newItem = document.createElement('li');
//作成したリストにクラス名をつける
newItem.classList.add('item');
//テキストを追加する
newItem.innerText = 'Item 3';
//ulのtodo-listに新しく作成したリストを追加する
todoList.appendChild(newItem);
//追加された要素は、反映されていない
console.log(nodeList);
console.log(nodeList.length); //-> 2
NodeListの方は、item 3が加えられましたが、リストの数は2つのまま。反映されていないことがわかります。
NodeListをHTMLCollectionへ変換する
面白いなと思ったのが、NodeListをHTMLCollectionへ変換できることです。
querySelectorで選んだものでも、todoList.childrenとすることによって、HTMLCollectionへ変換されています。
こうすることで、追加された要素を自動で反映することが出来ます。
const todoList = document.querySelector('#todo-list');
const items = todoList.children;
//todoListのchildエレメントを見てみる
console.log(items); //-> HTMLCollectionへ変換されている
//新しいリストを作成する
const newItem = document.createElement('li');
//作成したリストにクラス名をつける
newItem.classList.add('item');
//テキストを追加する
newItem.innerText = 'Item 3';
//ulのtodo-listに新しく作成したリストを追加する
todoList.appendChild(newItem);
//追加された要素が反映
console.log(items);
console.log(items.length); //-> 3
まとめ
ここまで理解できるのに、数日かかりました。
DevEdの動画を数回視聴を見て、こちらのお二人のブログのコードを参考にして、ChatGPTに質問し、時間がかかりました〜。
次は、DOMイベントについてです。引き続き、頑張りろう!(自分に言い聞かせてます)
【参考にさせていただいたブログ記事】
▶︎ HTMLCollectionとNodeListの違い:https://react-js.jp/htmlcollection-nodelist#toc6
▶︎【JavaScript】HTMLCollectionとNodeListの違い:https://web-engineer-wiki.com/javascript/htmlcollection-nodelist-diff/#index_id2