11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ParentNode.childrenをfor...ofループするときの注意点

Posted at
  • 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 だが、 HTMLCollectioniterable ではない
    • たとえば下記のコードを Babel with babel-polyfill で ES2015 にトランスパイルして動作させた場合、 HTMLCollection[Symbol.iterator]() がないため エラーになる
// 仕様上は動作しない
// (ただ 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 を直でさわらない時代だし、雑学的に知っておくとよいかもくらいな話です。

参考文献

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?