2015/04/10

getElementsByTagNameとquerySelectorAllの違い

photo by Tim Johnson

querySelectorAllという存在を知り、さっそくToDoアプリでgetElementsByTagNameを使っているところを書き換えようとしたとき、問題が発生した。

それは、以下の違いによるものだった。
  • getElementsByTagName : 動的なNodeList
  • querySelectorAll : 静的なNodeList


追記:2015/04/25 16:40
コメントで指摘していただいた箇所について追記する。ご指摘ありがとうございました!
※ コメント通知で気づいた時にはすでに削除?されていたっぽいですが

getElementByTagName について

ゆくゆくは、NodeListではなくHTMLCollection が返ってくるようになる。
どちらもcollectionに変わりないが、メソッドが少し違うので注意。



具体例をあげて違いをまとめていく。
ついでにquerySelectorAllの代替案として、ID指定のgetElementsByTagNameについても載せておく。



HTML


以降、このHTMLをもとにして進めていく。

<!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>
view raw index.html hosted with ❤ by GitHub


getElementsByTagName


getElementsByTagNameは、指定したタグ名を持つリスト(NodeList)を取得するメソッド。

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);
しかし、このままではHTMLにある全てのタグ要素を取得してしまう。
それを解決するために、querySelectorAllを使用する。

querySelectorAll


querySelectorAllは、CSSセレクタにマッチする要素のリスト(NodeList)を取得するメソッド。
jQueryで「$(‘#id > li’)」みたいに書く感覚だ。

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を取得したい場合は、以下のようにすればよい。

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);
view raw IdAndTagName.js hosted with ❤ by GitHub

ちょっと長くなってしまうが、getElementByIdとgetElementsByTagNameを併用すればよい。
他にもgetElementsByClassNameなどとも併用できる。




以上

written by @bc_rikko

2 件のコメント :

  1. 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等で配列化してしまえば意味ないんですけどね。参考までに。

    返信削除
    返信
    1. ご指摘ありがとうございます!
      内容は、本文に追記いたしました。

      ちょっとした手違いでコメントを非公開にしてしまい、今まで削除されたものだと勘違いしておりました。
      申し訳ございません。

      削除