JS:Array.forEachを使用してgetElementsByClassNameの結果を反復する


240

一部のDOM要素を反復処理したいので、次のようにします。

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});

しかし、エラーが発生します:

document.getElementsByClassName( "myclass")。forEachは関数ではありません

私はFirefox 3を使用しているので、getElementsByClassNameとの両方Array.forEachが存在することがわかります。これはうまくいきます:

[2, 5, 9].forEach( function(element, index, array) {
  //do stuff
});

getElementsByClassName配列の結果ですか?そうでない場合、それは何ですか?

回答:


384

いいえ。DOM4指定されているように、それはHTMLCollection(最新のブラウザーでは少なくとも。古いブラウザーはを返しましたNodeList)です。

最近のすべてのブラウザー(他のほとんどすべてのIE <= 8)では、ArrayのforEachメソッドを呼び出して、要素のリスト(HTMLCollectionまたはNodeList)をthis値として渡すことができます。

var els = document.getElementsByClassName("myclass");

Array.prototype.forEach.call(els, function(el) {
    // Do stuff here
    console.log(el.tagName);
});

// Or
[].forEach.call(els, function (el) {...});

ES6を使用できるという幸せな立場にある場合(つまり、Internet Explorerを安全に無視できるか、ES5トランスパイラーを使用している場合)、次を使用できますArray.from

Array.from(els).forEach((el) => {
    // Do stuff here
    console.log(el.tagName);
});

29
最初に配列に変換する必要はありません。だけを使用してください[].forEach.call(elsArray, function () {...})
わかった-SEは悪

1
NodeListではありません。それは配列のようなオブジェクトです。インスタンスタイプがあるとは思いません。querySelectorAllただし、メソッドはNodeListを返します。
Maksim Vi。

2
@MaksimVi。あなたは完全に正しい:DOM4 document.getElementsByClassName()はを返すように指定しますHTMLCollection(これは非常によく似ていますが、NodeListではありません)。間違いを指摘してくれてありがとう。
Tim Down

@MaksimVi .:どこかで変わったのかしら。私はいつもこれらのものをチェックします。
Tim Down

1
@TimDown、ヒントをありがとうHTMLCollection。ようやくHTMLCollection.prototype.forEach = Array.prototype.forEach;コードで使用できるようになりました。
Maksim Vi。

70

を使用Array.fromして、コレクションを配列に変換できますArray.prototype.forEach.call

Array.from(document.getElementsByClassName("myclass")).forEach(
    function(element, index, array) {
        // do stuff
    }
);

をサポートしていない古いブラウザではArray.from、Babelのようなものを使用する必要があります。


ES6では、次の構文も追加されています。

[...document.getElementsByClassName("myclass")].forEach(
    (element, index, array) => {
        // do stuff
    }
);

...配列自体だけでなく、すべての配列のようなオブジェクトで機能する構造化を休憩してから、古き良き配列構文を使用して値から配列を構築します。


代替関数querySelectorAll(これはちょっとgetElementsByClassName時代遅れになっています)は、forEachネイティブに持っているコレクションを返しますが、mapまたはのような他のメソッドfilterが欠落しているので、この構文はまだ役に立ちます。

[...document.querySelectorAll(".myclass")].map(
    (element, index, array) => {
        // do stuff
    }
);

[...document.querySelectorAll(".myclass")].map(element => element.innerHTML);

6
注:推奨されるトランスパイルなし(Babel)、これはIE <Edge、Opera、Safari <9、Androidブラウザー、Chrome for Androidなどでは互換性がありません)出典:mozilla dev docs
Sean

30

または、NodeListquerySelectorAllを返すwhich を使用できます。

document.querySelectorAll('.myclass').forEach(...)

最新のブラウザー(IEを除くEdgeを含む)でサポート:
querySelectorAll NodeList.prototype.forEach()を使用できますか

MDN: Document.querySelectorAll()


4
getElementByClassNameよりパフォーマンスが低下することに注意してください
SzabolcsPáll18年

3
パフォーマンスのペナルティは、 DOMの変更のような他のより集中的なタスクと比較してごくわずかです。これらの60,000を1ミリ秒で実行すると、合理的な使用法では問題にならないと確信しています:)
icl7126

1
間違ったベンチマークをリンクしました。これが正しい1つの対策ですthat.net/Benchmarks/Show/4076/0/…ローエンド電話で実行しただけで、160k / s対380k / sになりました。DOMの操作について説明したので、これもmeasurethat.net/Benchmarks/Show/5705/0/…の場合、 50k / sと130k / sの比較です。ご覧のとおり、NodeListが静的であるため(おそらく他の人が言及しているように)、DOMの操作はさらに遅くなります。ほとんどのユースケースではまだ無視できますが、それでもほぼ3倍遅くなります。
SzabolcsPáll19年

14

編集:新しいバージョンのHTMLでは戻り値の型が変更されていますが(Tim Downの最新の回答を参照)、以下のコードは引き続き機能します。

他の人が言ったように、それはNodeListです。ここにあなたが試すことができる完全で実用的な例があります:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script>
            function findTheOddOnes()
            {
                var theOddOnes = document.getElementsByClassName("odd");
                for(var i=0; i<theOddOnes.length; i++)
                {
                    alert(theOddOnes[i].innerHTML);
                }
            }
        </script>
    </head>
    <body>
        <h1>getElementsByClassName Test</h1>
        <p class="odd">This is an odd para.</p>
        <p>This is an even para.</p>
        <p class="odd">This one is also odd.</p>
        <p>This one is not odd.</p>
        <form>
            <input type="button" value="Find the odd ones..." onclick="findTheOddOnes()">
        </form>
    </body>
</html>

これは、IE 7、FF 5、Safari 5、およびWin 7上のChrome 12で機能します。


9

の結果はgetElementsByClassName()配列ではなく、配列のようなオブジェクトです。具体的にはと呼ばれ、HTMLCollection混乱しないようにしてくださいNodeList独自のforEach()メソッドがあります)。

ES2015で配列のようなオブジェクトを使用するために変換する簡単な方法の1つArray.prototype.forEach()は、まだ言及されていませんが、spreadオペレーターまたはspread構文を使用することです。

const elementsArray = document.getElementsByClassName('myclass');

[...elementsArray].forEach((element, index, array) => {
    // do something
});

2
これは、最新のブラウザでこれを行うには本当に正しい方法だと思います。これは、解決するために作成された正確なユースケーススプレッド構文です。
Matt Korostoff 2017年

5

getElementsByClassNameの結果は配列ですか?

番号

そうでない場合、それは何ですか?

複数の要素を返すすべてのDOMメソッドと同様に、これはNodeListです。https: //developer.mozilla.org/en/DOM/document.getElementsByClassNameを参照してください


3

すでに述べたように、次のように定義されgetElementsByClassNameHTMLCollectionを返します

[Exposed=Window]
interface HTMLCollection {
  readonly attribute unsigned long length;
  getter Element? item(unsigned long index);
  getter Element? namedItem(DOMString name);
};

以前は、一部のブラウザが代わりにNodeListを返していました。

[Exposed=Window]
interface NodeList {
  getter Node? item(unsigned long index);
  readonly attribute unsigned long length;
  iterable<Node>;
};

DOM4はNodeListを反復可能として定義するようになったため、違いは重要です。

Web IDLドラフトによると、

反復可能であると宣言されているインターフェースを実装するオブジェクトは、一連の値を取得するために反復されます。

:ECMAScript言語バインディングでは、反復可能なインターフェースのインターフェースプロトタイプオブジェクトに、「entries」、「forEach」、「keys」、「values」、および @@ iteratorプロパティがあります

つまり、を使用したい場合は、のようにNodeListforEachを返すDOMメソッドを使用できます。querySelectorAll

document.querySelectorAll(".myclass").forEach(function(element, index, array) {
  // do stuff
});

これはまだ広くサポートされていません。Node.childNodesのforEachメソッドも参照してください


1
Chrome 49リターンforEach in not a function
Vitaly Zdanevich

@VitalyZdanevich Try Chromium 50
Oriol

Chrome 50では、次のようになりますdocument.querySelectorAll(...).forEach is not a function
Vitaly Zdanevich

これは、クロム50に働いた、と今でも多分それは安定した十分にはクローム50に出荷されると考えられていなかったクロム53上で動作します@VitalyZdanevich
オリオール


1

これはより安全な方法です:

var elements = document.getElementsByClassName("myclass");
for (var i = 0; i < elements.length; i++) myFunction(elements[i]);

0

getElementsByClassName最新のブラウザでHTMLCollectionを返します。

これは 、ループによって反復可能な引数に似た配列のようなオブジェクトでありfor...ofMDN docがそれについて言っている下記を参照してください。

以下のために...声明のは、反復可能なオブジェクトを反復ループ作成し、組み込みの文字列、配列、:を含む、アレイのようなオブジェクト(例えば、引数 またはのNodeList)、TypedArray、地図、セット、およびユーザー定義の反復可能オブジェクトを。オブジェクトの各プロパティの値に対して実行されるステートメントでカスタム反復フックを呼び出します。

for (let element of getElementsByClassName("classname")){
   element.style.display="none";
}

それほど、活字によると:error TS2488: Type 'HTMLCollectionOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.
カメが可愛い

@TurtlesAreCute、ここでOPはtypescriptではなくjavascriptを使用しており、バニラjsの推奨に従って回答したので、typescriptでは問題の異なる解決策になる可能性があります。
Haritsinh Gohil

@TurtlesAreCute、ちなみにtypescriptでも機能していますが、特定のcssクラスの要素を保持する変数の正しいタイプに言及する必要があるため、それに応じてキャストできます。詳しくはこの回答を参照してください。
Haritsinh Gohil

0

ここに私がjsperfで作成したテストがありますhttps ://jsperf.com/vanillajs-loop-through-elements-of-class

ChromeとFirefoxの最もパフォーマンスの高いバージョンは、document.getElementsByClassNameと組み合わせた古き良きforループです。

var elements = document.getElementsByClassName('testClass'), elLength = elements.length;
for (var i = 0; i < elLength; i++) {
    elements.item(i).textContent = 'Tested';
};

Safariでは、このバリアントが勝者です。

var elements = document.querySelectorAll('.testClass');
elements.forEach((element) => {
    element.textContent = 'Tested';
});

すべてのブラウザーで最もパフォーマンスの高いバリアントが必要な場合は、次のようになります。

var elements = document.getElementsByClassName('testClass');
Array.from(elements).map(
    (element) => {
        return element.textContent = 'Tested';
    }
);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.