HTMLCollection要素のforループ


405

私はすべての要素のIDを取得するように設定しようとしていHTMLCollectionOfます。次のコードを書きました。

var list = document.getElementsByClassName("events");
console.log(list[0].id);
for (key in list) {
    console.log(key.id);
}

しかし、私はコンソールで次の出力を得ました:

event1
undefined

これは私が期待したものではありません。2番目のコンソール出力はなぜundefined最初のコンソール出力はevent1なぜですか?


23
質問がfor ...についての文であるのに、なぜタイトルは「foreach」と言うのですか グーグルで偶然ここに来ました。
mxt3

@ mxt3まあ、私の理解では、それはJavaのfor-eachループのアナログでした(同じように動作しました)。

1
@ mxt3同じことを考えました!:しかし、受け入れ答えて読んだ後、私はArray.from()を使用して、私のforeach問題を解決し、このラインを見つけたArray.from(document.getElementsByClassName("events")).forEach(function(item) {
HoldOffHunger

回答:


839

元の質問に答えて、あなたはfor/in間違って使用しています。あなたのコードでkeyは、インデックスです。したがって、疑似配列から値を取得するには、実行する必要がlist[key]あり、IDを取得するには、実行する必要がありますlist[key].id。しかし、for/inそもそもこれを行うべきではありません。

概要(2018年12月に追加)

for/innodeListやHTMLCollectionの反復には使用しないでください。これを回避する理由は次のとおりです。

最近のブラウザー(Safari、Firefox、Chrome、Edge)の最近のバージョンはすべて、またはのfor/ofようなDOMリストの反復をサポートしています。nodeListHTMLCollection

次に例を示します。

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}

古いブラウザ(IEなどを含む)を含めるには、これはどこでも機能します。

var list= document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
    console.log(list[i].id); //second console output
}

使用してはいけない理由の説明 for/in

for/inオブジェクトのプロパティを繰り返すためのものです。つまり、オブジェクトのすべての反復可能なプロパティを返します。配列(配列要素または疑似配列要素を返す)で機能しているように見えるかもしれませんが、配列のような要素からは期待されていないオブジェクトの他のプロパティを返すこともあります。そして、オブジェクトHTMLCollectionまたはnodeListオブジェクトの両方が、for/in反復で返される他のプロパティを持つことができると思います。私はこれをChromeで試してみて、リスト内の項目(インデックス0、1、2など)を取得する方法で繰り返しますが、lengthおよびitemプロパティも取得します。for/in反復は単にHTMLCollectionのために動作しません。


でHTMLCollectionを反復できない理由については、http: //jsfiddle.net/jfriend00/FzZ2H/を参照してくださいfor/in

Firefoxでは、for/inイテレーションは次の項目(オブジェクトのすべての反復可能なプロパティ)を返します。

0
1
2
item
namedItem
@@iterator
length

うまくいけば、今度は、for (var i = 0; i < list.length; i++)代わりにを使用したい理由がわかるので01を取得2し、イテレーションで使用できます。


以下は、2015年から2018年にかけてブラウザがどのように進化したかを示したものであり、反復するための追加の方法を提供します。上記のオプションを使用できるため、現在のブラウザではこれらは必要ありません。

2015年のES6のアップデート

ES6に追加されたのはArray.from()、配列のような構造を実際の配列に変換することです。これにより、次のようにリストを直接列挙できます。

"use strict";

Array.from(document.getElementsByClassName("events")).forEach(function(item) {
   console.log(item.id);
});

作業デモ(2016年4月現在のFirefox、Chrome、Edge):https : //jsfiddle.net/jfriend00/8ar4xn2s/


2016年のES6のアップデート

これをコードに追加するだけで、ES6 for / ofコンストラクトをa NodeListとan で使用できるようにHTMLCollectionなります。

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

次に、次のことができます。

var list = document.getElementsByClassName("events");
for (var item of list) {
    console.log(item.id);
}

これは、Chrome、Firefox、およびEdgeの現在のバージョンで機能します。これは、ArrayイテレータをNodeListプロトタイプとHTMLCollectionプロトタイプの両方に接続し、for / ofを反復するときに、Arrayイテレータを使用してそれらを反復するため機能します。

作業デモ:http : //jsfiddle.net/jfriend00/joy06u4e/


2016年12月のES6の2回目の更新

2016年12月の時点で、Symbol.iteratorサポートはChrome v54およびFirefox v50に組み込まれているため、以下のコードはそれ自体で機能します。Edgeにはまだ組み込まれていません。

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}

作業デモ(ChromeおよびFirefox):http : //jsfiddle.net/jfriend00/3ddpz8sp/

2017年12月のES6の3番目のアップデート

2017年12月の時点では、この機能のためのエッジ41.16299.15.0で作品nodeListのようdocument.querySelectorAll()ではなく、HTMLCollectionのようにdocument.getElementsByClassName()あなたが持っているので、手動のためにエッジにそれを使用するイテレータを割り当てますHTMLCollection。あるコレクションタイプを修正し、他のコレクションタイプを修正しないのは、完全な謎です。ただし、現在のバージョンのEdge document.querySelectorAll()では、少なくともES6 for/of構文の結果を使用できます。

上記のjsFiddleも更新して、両方HTMLCollectionnodeList個別にテストし、jsFiddle自体に出力をキャプチャします。

2018年3月のES6の4番目のアップデート

mesqueeebごとSymbol.iteratorに、Safariにもサポートが組み込まれているfor (let item of list)ため、document.getElementsByClassName()またはのどちらでも使用できますdocument.querySelectorAll()

2018年4月のES6の5番目のアップデート

どうやら、HTMLCollectionwith 反復のサポートはfor/of2018年秋にEdge 18で行われる予定です。

2018年11月のES6の6番目のアップデート

Microsoft Edge v18(2018年秋のWindows Updateに含まれています)では、HTMLCollectionとNodeListの両方をEdgeのfor / ofで反復できることを確認できます。

そのため、現在のすべてのブラウザーにはfor/of、HTMLCollectionオブジェクトとNodeListオブジェクトの両方の反復に対するネイティブサポートが含まれています。


1
JSが更新されたため、非常に詳細な更新をありがとうございます。これは、初心者が特定のJSリリースにのみ適用される他の記事/投稿のコンテキストでこのアドバイスを理解するのに役立ちます。
brownmagik352

卓越した答え、素晴らしいアップデートのサポート..ありがとう。
コサックマン

79

for/ またはで/ inを使用することはできません。ただし、いくつかのメソッドを使用できます。ただし、それらを使用して、またはとして渡すことができます。NodeListHTMLCollectionArray.prototype.call()NodeListHTMLCollectionthis

したがって、jfriend00のforループの代わりとして、以下を検討してください

var list= document.getElementsByClassName("events");
[].forEach.call(list, function(el) {
    console.log(el.id);
});

この手法をカバーするMDNに関する良い記事があります。ただし、ブラウザの互換性に関する警告に注意してください。

[...]ホストオブジェクト(などNodeList)をthisネイティブメソッド(など)に 渡すことforEachは、すべてのブラウザでの動作が保証されておらず、一部のブラウザでは失敗することがわかっています。

したがって、このアプローチは便利ですが、forループは最もブラウザ互換なソリューションである可能性があります。

アップデート(2014年8月30日):最終的にES6forofを使用できるようになります/

var list = document.getElementsByClassName("events");
for (const el of list)
  console.log(el.id);

ChromeとFirefoxの最近のバージョンではすでにサポートされています。


1
非常に素晴らしい!この手法を使用して、選択したオプションの値をから取得しました<select multiple>。例[].map.call(multiSelect.selectedOptions, function(option) { return option.value; })
XåpplI'-I0llwlg'I -

1
これに対するES2015ソリューションを探していたので、そのfor ... of動作を確認していただきありがとうございます。
Richard Turner

60

ES6では、あなたのような何かを行うことができ[...collection]、またはArray.from(collection)

let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//or
Array.from(collection).forEach(someFn)

例えば:-

    navDoms = document.getElementsByClassName('nav-container');
    Array.from(navDoms).forEach(function(navDom){
     //implement function operations
    });

@DanielMは、私が行ったことは、配列のような構造の浅いクローンであると推測します
mido

:今、私は私が探していた文書を発見した-私は、感謝を参照してくださいdeveloper.mozilla.org/en/docs/Web/JavaScript/Reference/...
DanielM

私はいつもこれを使用しています。Array.fromよりもはるかに簡単です。パフォーマンスやメモリにかなりの欠点があるのか​​と思います。たとえば、テーブルの行のセルを反復処理する必要がある場合は、[...row.cells].forEach代わりにを使用しますrow.querySelectorAll('td')
Mojimi

16

次の2行を追加できます。

HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.forEach = Array.prototype.forEach;

HTMLCollectiongetElementsByClassNameおよびgetElementsByTagNameによって返されます

NodeListquerySelectorAllによって返されます

このようにforEachを実行できます:

var selections = document.getElementsByClassName('myClass');

/* alternative :
var selections = document.querySelectorAll('.myClass');
*/

selections.forEach(function(element, i){
//do your stuffs
});

この答えはとても効果的です。キャッチとは何ですか?
Peheje

2
問題は、このソリューションがIE11では機能しないことです。しかし良い解決策。
Rahul Gaba

2
NodeList既に持ってforEach()いることに注意しください。
フランクリンYu

7

IE 11Firefox 49で forEachを使用するときに問題が発生しました

このような回避策を見つけました

Array.prototype.slice.call(document.getElementsByClassName("events")).forEach(function (key) {
        console.log(key.id);
    }

IE11の優れたソリューション!
以前

6

代わりにArray.from使用することですArray.prototype.forEach.call

forEach: Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });

地図: Array.prototype.map.call(htmlCollection, i => { console.log(i) });

ect ...


6

forIE9以降を使用している場合、es6機能を使用してループをエスケープする理由はありません。

ES5には、2つの適切なオプションがあります。まず、あなたができる「借り」はArray「S forEachエヴァンは言及します

しかし、さらに良い...

使用Object.keys()持っているforEachとフィルタ自動的に「独自のプロパティ」へ。

つまり、Object.keysをで実行することfor... inと本質的に同等ですが、HasOwnPropertyはるかにスムーズです。

var eventNodes = document.getElementsByClassName("events");
Object.keys(eventNodes).forEach(function (key) {
    console.log(eventNodes[key].id);
});

5

2016年3月の時点で、Chrome 49.0では次の環境でfor...of動作しHTMLCollectionます。

this.headers = this.getElementsByTagName("header");

for (var header of this.headers) {
    console.log(header); 
}

こちらのドキュメントをご覧ください。

ただし、を使用する前に次の回避策を適用した場合にのみ機能しますfor...of

HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

同じことがfor...ofwith で使用するために必要ですNodeList

NamedNodeMap.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

for...of上記の回避策なしですぐに機能することを期待しています。未解決の問題はこちらです:

https://bugs.chromium.org/p/chromium/issues/detail?id=401699

更新:以下のExpenzorのコメントを参照してください。これは2016年4月の時点で修正されています。HTMLCollection.prototype[Symbol.iterator] = Array.prototype [Symbol.iterator];を追加する必要はありません。for ... ofを使用してHTMLCollectionを反復処理する


4
これは、あなたが追加する必要はありません4月2016のように固定されたHTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];上で反復するHTMLCollectionfor...of
Expenzor 2016年

3

端に

if(!NodeList.prototype.forEach) {
  NodeList.prototype.forEach = function(fn, scope) {
    for(var i = 0, len = this.length; i < len; ++i) {
      fn.call(scope, this[i], i, this);
    }
  }
}

2

いつも使う簡単な回避策

let list = document.getElementsByClassName("events");
let listArr = Array.from(list)

この後、選択に対して任意の配列メソッドを実行できます

listArr.map(item => console.log(item.id))
listArr.forEach(item => console.log(item.id))
listArr.reverse()

1

ESの古いバージョン(ES5など)を使用する場合は、次のように使用できますas any

for (let element of elementsToIterate as any) {
      console.log(element);
}

0

に変更したい

var list= document.getElementsByClassName("events");
console.log(list[0].id); //first console output
for (key in list){
    console.log(list[key].id); //second console output
}

5
参考までに、なぜこれが正しく機能しないのか、私の答えを見てください。for (key in list)いくつかのプロパティが返されますHTMLCollection、コレクション内の項目であることを意味していないことを。
jfriend00 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.