querySelectorAllという存在を知り、さっそくToDoアプリでgetElementsByTagNameを使っているところを書き換えようとしたとき、問題が発生した。
それは、以下の違いによるものだった。
- getElementsByTagName : 動的なNodeList
- querySelectorAll : 静的なNodeList
追記:2015/04/25 16:40
コメントで指摘していただいた箇所について追記する。ご指摘ありがとうございました!
※ コメント通知で気づいた時にはすでに削除?されていたっぽいですが
getElementByTagName について
- 日本語MDN・DOM3 :
NodeList
が返ってくる - US版MDN・DOM4 :
HTMLCollection
が返ってくる
ゆくゆくは、NodeListではなくHTMLCollection が返ってくるようになる。
どちらもcollectionに変わりないが、メソッドが少し違うので注意。
具体例をあげて違いをまとめていく。
ついでにquerySelectorAllの代替案として、ID指定のgetElementsByTagNameについても載せておく。
HTML
以降、このHTMLをもとにして進めていく。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ja"> | |
<head> | |
<meta charset='UTF-8' /> | |
<title>getElementsByTagName - querySelectorAll</title> | |
</head> | |
<body> | |
<ul id="list1"> | |
<li>List1_Item1</li> | |
</ul> | |
<ul id="list2"> | |
<li>List2_Item1</li> | |
</ul> | |
<script src="app.js"></script> | |
</body> | |
</html> |
getElementsByTagName
getElementsByTagNameは、指定したタグ名を持つリスト(NodeList)を取得するメソッド。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var tagName = document.getElementsByTagName('li'); | |
// 2 | |
console.log(tagName.length); | |
var list1Item = document.createElement('li'); | |
list1Item.innerText = 'List1_Item2'; | |
document.getElementById('list1').appendChild(list1Item); | |
var list2Item = document.createElement('li'); | |
list2Item.innerText = 'List2_Item2'; | |
document.getElementById('list2').appendChild(list2Item); | |
// 4(動的なNodeList) | |
console.log(tagName.length); |
それを解決するために、querySelectorAllを使用する。
querySelectorAll
querySelectorAllは、CSSセレクタにマッチする要素のリスト(NodeList)を取得するメソッド。
jQueryで「$(‘#id > li’)」みたいに書く感覚だ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var querySelector = document.querySelectorAll('#list1 > li'); | |
// 1 | |
console.log(querySelector.length); | |
var list1Item = document.createElement('li'); | |
list1Item.innerText = 'List1_Item2'; | |
document.getElementById('list1').appendChild(list1Item); | |
var list2Item = document.createElement('li'); | |
list2Item.innerText = 'List2_Item2'; | |
document.getElementById('list2').appendChild(list2Item); | |
// 1(静的なNodeList) | |
console.log(querySelector.length); |
うまい具合にID指定をしつつ、LI要素を取得することができる。
しかし、前述の通り、querySelectorAllは「静的なNodeList」のため、取得した時点でのNodeListを保持してしまう。
そのため、サンプルにあるように、要素を追加してもlengthが1のままになってしまう。
IDを指定したい。でも動的なNodeListが欲しいという場合の対応法について、次で説明する。
ID指定のgetElementsByTagName
IDを指定しつつ、動的なNodeListを取得したい場合は、以下のようにすればよい。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var list = document.getElementById('list1').getElementsByTagName('li'); | |
// 1 | |
console.log(list.length); | |
var list1Item = document.createElement('li'); | |
list1Item.innerText = 'List1_Item2'; | |
document.getElementById('list1').appendChild(list1Item); | |
var list2Item = document.createElement('li'); | |
list2Item.innerText = 'List2_Item2'; | |
document.getElementById('list2').appendChild(list2Item); | |
// 2(動的なNodeList) | |
console.log(list.length); |
ちょっと長くなってしまうが、getElementByIdとgetElementsByTagNameを併用すればよい。
他にもgetElementsByClassNameなどとも併用できる。
以上
written by @bc_rikko
HTMLのcollectionでハマりやすいところを捉えたいい記事だと思います。
返信削除ただ、一つ気になるのは、動的なNodeListと静的なNodeListとされている点です。
確かに、NodeListには静的なものと動的なものが有ります。
->https://developer.mozilla.org/ja/docs/Web/API/NodeList
childNodesなどが動的なもの、querySelectorAllなどが静的なものとされています。
ただ、getElementsByTagNameで取得したものは、NodeListではなく、HTMLCollectionです。
MDNの日本語版やDOM3ではNodeListが返ってくることになっていますが、
MDNのUS版や、DOM4では、HTMLCollectionが返ることになっています。
->http://www.w3.org/TR/domcore/
FirefoxやChrome等のブラウザの実装もそれに追従しています。
NodeListもHTMLCollectionもcollectionには変わりないのですが、メソッドが少し違うという点があります。
どちらもArray.from等で配列化してしまえば意味ないんですけどね。参考までに。
ご指摘ありがとうございます!
削除内容は、本文に追記いたしました。
ちょっとした手違いでコメントを非公開にしてしまい、今まで削除されたものだと勘違いしておりました。
申し訳ございません。