-
for (const el of parentElement.children)
は動かないものと考える- 原因と対策について解説
ES2015 おさらい
for...of ループ
-
iterable なオブジェクトを1件ずつ処理するためのループ構文
-
for each
的なやつ
-
var elements = document.querySelectorAll('.item');
// for...in だと hasOwnProperty とかややこしい
for (const i in elements) {
if (elements.hasOwnProperty(i)) {
const el = elements[i];
...
}
}
// forEach() メソッドは Array にしかない
//elements.forEach((el) => { ... });
Array.prototype.forEach.call(elements, (el) => {
...
});
// そこで for...of
for (const el of elements) {
...
}
iterable
-
for...of
文に渡すことができるオブジェクトのプロトコル (インターフェイスのようなもの)-
String
,Array
,Map
,Set
などのループできそうなオブジェクトが該当 -
[Symbol.iterator]
メソッドを呼び出すと iterator オブジェクトが取得できる
-
const items = ['one', 'two', 'three'];
const iter = items[Symbol.iterator]();
console.log(iter.next()); // { value: "one", done: false }
console.log(iter.next()); // { value: "two", done: false }
console.log(iter.next()); // { value: "three", done: false }
console.log(iter.next()); // { value: undefined, done: true }
NodeList と HTMLCollection
-
document.querySelectorAll()
などの戻り値はNodeList
オブジェクト -
ParentElement.children
の戻り値はHTMLCollection
オブジェクト - どちらも似たような、DOM要素の配列
-
NodeList
は iterable だが、HTMLCollection
は iterable ではない- たとえば下記のコードを Babel with babel-polyfill で ES2015 にトランスパイルして動作させた場合、
HTMLCollection
に[Symbol.iterator]()
がないため エラーになる
- たとえば下記のコードを Babel with babel-polyfill で ES2015 にトランスパイルして動作させた場合、
// 仕様上は動作しない
// (ただ Google Chrome だと HTMLCollection も iterable 化されてるので動く)
for (const el of parentElement.children) {
...
}
対策
- Array に変換するのが手っ取り早い
// とりあえず Array に変換
for (const el of Array.from(parentElement.children)) {
...
}
// だったら forEach でもいいや感 key/value つかえるメリットはある
Array.from(parentElement.children).forEach((el) => {
...
});
- どうしてもやりたいなら
HTMLCollection
のプロトタイプを書き換えて iterable に
if (HTMLCollection.prototype[Symbol.iterator] === undefined) {
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
}
まあ DOM を直でさわらない時代だし、雑学的に知っておくとよいかもくらいな話です。