JavaScript の for...of 周りとDOMの取得周りのメモ
2021/09/03
jQuery と JavaScript の要素の扱い
jQuery
console.log($("img"));
// jQuery.fn.init(4) [img.img-fluid, img.img-fluid, img.img-fluid, img.img-fluid, prevObject: jQuery.fn.init(1)]
// 0: img.img-fluid
// 1: img.img-fluid
// 2: img.img-fluid
// 3: img.img-fluid
// length: 4
// prevObject: jQuery.fn.init [document]
// [[Prototype]]: Object(0)
JS
const images = document.images;
console.log(images);
// HTMLCollection(4)
// 0: img.img-fluid
// 1: img.img-fluid
// 2: img.img-fluid
// 3: img.img-fluid
// length: 4
// [[Prototype]]: HTMLCollection
const imgTags = document.querySelectorAll('img');
console.log(imgTags);
// NodeList(4)
// 0: img.img-fluid
// 1: img.img-fluid
// 2: img.img-fluid
// 3: img.img-fluid
// length: 4
// [[Prototype]]: NodeList
jQuery は jQueryオブジェクト、 document.images では HTMLCollections、 document.querySelectorAll('img') では NodeList が取得できる。
- NodeListとHTMLCollectionも別物なので気を付けよう。(DOMおれおれAdvent Calendar 2015 – 13日目) | Ginpen.com
- HTMLCollectionとNodeListの違いについて - Engineers Party
分割代入と for...of Object.entries()
const imgTags = document.querySelectorAll('img');
for (const [key, val] of Object.entries(imgTags)) { /* 略 */ }
imgTagsは[key, val]の2つの値に分割代入できるようなイテラブルな形ではないので、Object.entries()でイテラブルな形に変換する- 変換されたキーと値の配列をそれぞれ
keyとvalの2つの変数に分割代入する
という流れ。ただ、 imgTags は場合によってはイテラブルな場合もある。
const imgs = document.images;
console.log(imgs)
for (const img of imgs) {
console.log(typeof img)
console.log(img)
}
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
const imgTags = document.querySelectorAll('img');
console.log(imgTags)
for (const img of imgTags) {
console.log(typeof img)
console.log(img)
}
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// object
// <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
for (const [key, val] of Object.entries(imgTags)) {
console.log(typeof key, typeof val)
console.log(key, val)
}
//string object
// 0 <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// string object
// 1 <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// string object
// 2 <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
// string object
// 3 <img src="https://placehold.jp/640x320.jpg" class="img-fluid">
上述の2番目のケースでは imgTags をそのまま for...of で回しているがイテラブル。
問題となるのは3番目のケースで分割代入する前提で回そうとした場合。
const imgTags = document.querySelectorAll('img');
for (const [key, val] of imgTags) {
console.log(typeof key, typeof val)
console.log(key, val)
}
// Uncaught TypeError: .for is not iterable
当然といえば当然ですが、単純な NodeList の imgTags を key, val の2つの値に分割代入しようとしてもそれはできないわけで。
そのため、 not iterable と怒られるわけです。
const imgTags = document.querySelectorAll('img');
console.log(imgTags);
// NodeList(4) [img.img-fluid, img.img-fluid, img.img-fluid, img.img-fluid]
// 0: img.img-fluid
// 1: img.img-fluid
// 2: img.img-fluid
// 3: img.img-fluid
// length: 4
// [[Prototype]]: NodeList
console.log(Object.entries(imgTags));
// (4) [Array(2), Array(2), Array(2), Array(2)]
// 0: (2) ["0", img.img-fluid]
// 1: (2) ["1", img.img-fluid]
// 2: (2) ["2", img.img-fluid]
// 3: (2) ["3", img.img-fluid]
// length: 4
// [[Prototype]]: Array(0)
Object.entries() で処理すると key, val のセット(配列)の配列の形になるので、これならばイテラブルとなるわけです。
改修
ただし、今回は key は使っておらず val のみの使用なので、実は分割代入がいらなかったという。
const imgTags = document.querySelectorAll('img');
for (const [key, val] of Object.entries(imgTags)) { /* 略 */ }
ではなく
const imgTags = document.querySelectorAll('img');
for (const imgTag of imgTags) { /* 略 */ }
これで事足りてしまったというオチ。
ただし、バベる場合は for...of はコードの肥大化につながるのでオススメされない。
実際、分割代入しないのであればわざわざ for...of を使用するまでもないのでは、ということで手を加えることに。
const imgTags = document.querySelectorAll('img');
Object.values(imgTags).map((imgTag) => { /* 略 */ });
結局 Object.values() は使ったものの、 Array.prototype.map() で処理できる。 forEach は遅いとどこかで見た気がするのでなるべく使わずに。
ただ、あまり考えなくても良いかもしれません。
参考
HTMLCollection, NodeList
- NodeListとHTMLCollectionも別物なので気を付けよう。(DOMおれおれAdvent Calendar 2015 – 13日目) | Ginpen.com
- HTMLCollectionとNodeListの違いについて - Engineers Party
for...of
- JavaScript で [key, value] のループと分割代入を使ったループ - Qiita
- for...of - JavaScript | MDN
- Object.entries() - JavaScript | MDN
- for...of を使うなってAirbnbが言ってたから使わないようにしてたら慣れた - Qiita