document.querySelectorAllの要素にforEachは使えない

document.querySelectorAllの要素にforEachは使えない

querySelectorAllにforEachは使えない

document.querySelectorAllはCSS セレクタにマッチする文書中の要素のすべてのリストを返す。

例えば下記のように記述すればclass="foo"にテキストを挿入することができる。

var el = document.querySelectorAll('.foo');
for(var i=0; i<el.length; i++) {
  el[i].textContent = 'bar' + i;
}

forだと可読性が良くないためより簡単に記述するためforEachを使用しているコードをたまに見かける。

var el = document.querySelectorAll('.foo');
el.forEach(function(el, i) {
  el.textContent = 'bar' + i;
});

しかし、forEachを使用するとChrome以外のブラウザではエラーになり動作しない。
2018年9月追記: 最新バージョンのChromeでは動作するよう修正されている。
Microsoft EdgeでのじゃforEach実行結果

なぜならdocument.querySelectorAllで取得したものはオブジェクトでありforEachは配列の各要素に対して一度ずつ実行するためのメソッドだからだ。

よってChromeでエラーが出ないのは本来の仕様とは異なる。

var el = document.querySelectorAll('.foo');
console.info(typeof el); // =>; object
el.forEach(function(el, i) {
  el.textContent = 'bar' + i;
});

しかしObject.keysとforEachを使用すれば処理できるので下記のように記述すればすべてのブラウザでエラーは発生しない。

// before
// <div class="hoge"></div>
// <div class="hoge"></div>
// <div class="hoge"></div>
var el2 = document.querySelectorAll('.hoge');
Object.keys(el2).forEach(function(i) {
  el2[i].textContent = 'fuga' + i;
});
// after
// <div class="hoge">fuga0</div>
// <div class="hoge">fuga1</div>
// <div class="hoge">fuga2</div>

// 用途によってはArray.fromが使える
var el3 = document.querySelectorAll('.bar');
Array.from(el3).forEach((x, i) => x.textContent = 'bar' + i);

ただし、iOS 9 Safariなどの古いブラウザだと前述の3つは使用できないため、ObjectではなくArrayに変換してからforeachを使用する必要がある。

// iOS 9 Safariのような古いブラウザでも使用可能な書き方
var el0 = [].slice.call(document.querySelectorAll('.baz'));
el0.forEach(function(el, i) {
  el.textContent = 'baz' + i;
});

document.querySelectorAllにforEachを使用したサンプル