JS: Array.forEach kullanarak getElementsByClassName sonucu üzerinden yineleme


240

Bazı DOM öğeleri üzerinde yineleme yapmak istiyorum, bunu yapıyorum:

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

ama bir hata alıyorum:

document.getElementsByClassName ("myclass"). forEach bir işlev değil

Ben de bunu biliyoruz bu yüzden Firefox 3 kullanıyorum getElementsByClassNameve Array.forEachmevcuttur. Bu iyi çalışıyor:

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

getElementsByClassNameBir Dizinin sonucu mu? Eğer değilse, nedir?

Yanıtlar:


384

Hayır . DOM4'te belirtildiği gibi , bir HTMLCollection(en azından modern tarayıcılarda, en azından Eski tarayıcılar a'yı döndürdü NodeList).

Tüm modern tarayıcılarda (hemen hemen diğer IE <= 8'de), Array'ın forEachyöntemini çağırabilir , değer olarak öğe listesini ( HTMLCollectionya da olsun NodeList) thisiletebilirsiniz:

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'yı kullanmaktan memnunsanız (yani Internet Explorer'ı güvenle yok sayabilirsiniz veya bir ES5 transpiler kullanıyorsanız), şunları kullanabilirsiniz Array.from:

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

29
Önce onu Array'a dönüştürmeye gerek yok. Sadece kullan [].forEach.call(elsArray, function () {...}).
kay - SE kötü

1
Bir DüğümListe DEĞİLDİR. Dizi benzeri bir nesne. Bunun bir örnek türü olduğunu bile düşünmüyorum. querySelectorAllyöntemi yine de bir NodeList döndürür.
Maksim Vi.

2
@MaksimVi. Kesinlikle haklısınız: DOM4 bunun document.getElementsByClassName()bir HTMLCollection(çok benzer ancak bir NodeList olmayan) döndürmesi gerektiğini belirtir . Hatayı belirttiğiniz için teşekkürler.
Tim Down

@MaksimVi .: Bunun bir noktada değişip değişmediğini merak ediyorum. Genellikle bunları kontrol ederim.
Tim Down

1
@TimDown, Tavsiye için teşekkürler HTMLCollection. Şimdi sonunda HTMLCollection.prototype.forEach = Array.prototype.forEach;kodumu kullanabilirsiniz .
Maksim Vi.

70

Sen kullanabilirsiniz Array.fromyolu temizleyici daha tatmin edebilmek için toplama dönüştürmek için Array.prototype.forEach.call:

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

Desteklemeyen eski tarayıcılarda Array.fromBabel gibi bir şey kullanmanız gerekir.


ES6 ayrıca bu sözdizimini ekler:

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

...Tüm diziye benzer nesneler üzerinde yapılan çalışmalarla yıkım , sadece dizileri değil, aynı zamanda değerlerden bir dizi oluşturmak için iyi eski dizi sözdizimi kullanılır.


Alternatif işlev querySelectorAll( getElementsByClassNameeski gibi), forEachyerel olan bir koleksiyonu döndürürken , diğer yöntemler gibi mapveya filtereksik, bu nedenle bu sözdizimi hala yararlıdır:

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

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

6
Not: önerildiği gibi transpilling olmadan (Babel), bu IE <Edge, Opera, Safari <9, Android tarayıcı, Android için Chrome, vb.) İle uyumlu DEĞİLDİR Kaynak: mozilla dev docs
Sean

30

Veya NodeListquerySelectorAll döndürenini kullanabilirsiniz :

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

Modern tarayıcılar tarafından desteklenir (Edge dahil ancak IE değil):
querySelectorAll
NodeList.prototype.forEach ()

MDN: Document.querySelectorAll ()


4
GetElementByClassName
Szabolcs Páll

3
Performans cezası, DOM'u değiştirmek gibi diğer daha yoğun görevlerle karşılaştırıldığında ihmal edilebilir . Ben yürütmek durumunda 1 milisaniye bunlardan 60,000'i , ben herhangi bir makul kullanım :) için bir sorun olmayacaktır eminim
icl7126

1
Yanlış karşılaştırmayı bağladınız. İşte doğru olanı Measethat.net/Benchmarks/Show/4076/0/… Sadece düşük kaliteli telefonumda koştu, 160k / s vs 380k / s aldım. Eğer DOM işlemleri söz beri, burada da olduğu measurethat.net/Benchmarks/Show/5705/0/... Got 50k / s vs 130k / s. Gördüğünüz gibi, büyük olasılıkla NodeList'in statik olması nedeniyle (başkaları tarafından belirtildiği gibi) DOM'yi manipüle etmek daha yavaştır. Çoğu kullanım durumunda hala pazarlık edilebilir, ancak yine de neredeyse 3 kat daha yavaştır.
Szabolcs Páll

14

Düzenleme: Dönüş türü HTML'nin yeni sürümlerinde değişmiş olsa da (bkz. Tim Down'ın güncellenmiş cevabı), aşağıdaki kod hala çalışır.

Diğerlerinin söylediği gibi, bu bir Düğüm Listesi. İşte deneyebileceğiniz eksiksiz, çalışan bir örnek:

<!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>

Bu, Win 7'de IE 9, FF 5, Safari 5 ve Chrome 12'de çalışır.


9

Sonucu getElementsByClassName()Array, ancak olmayan dizi benzeri bir nesne . Özellikle buna HTMLCollectionkarıştırılmamalıdır NodeList( kendi forEach()yöntemi vardır ).

ES2015 ile dizi benzeri bir nesneyi Array.prototype.forEach()henüz kullanılmayan bir kullanım için dönüştürmenin basit bir yolu , yayma işlecini veya yayılma sözdizimini kullanmaktır :

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

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

2
Bunun modern tarayıcılarda bunu yapmanın gerçekten doğru yolu olduğunu hissediyorum. Bu tam kullanım senaryo yayılımı sözdizimini çözmek için oluşturuldu.
Matt Korostoff


3

Daha önce de belirtildiği gibi, HTMLColectiongetElementsByClassName döndürür .

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

Önceden, bazı tarayıcılar bunun yerine bir NodeList döndürüyordu .

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

Fark önemlidir, çünkü DOM4 artık NodeList'i tanımlamaktadır yinelenebilir olarak tanımlamaktadır.

Göre Web IDL taslak,

Yinelenebilir olduğu bildirilen bir arabirimi uygulayan nesneler, bir dizi değer elde etmek için yinelenmeyi destekler.

Not : ECMAScript dil bağlamasında, yinelenebilir bir arabirim , arabirim prototip nesnesinde “girişler”, “forEach”, “anahtarlar”, “değerler” ve @@ yineleyici özelliklerine sahip olacaktır .

Kullanmak istediğiniz takdirde, Bunun anlamı forEach, bir döndüren bir DOM yöntemi kullanabilirsiniz nodelist gibi querySelectorAll.

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

Bunun henüz geniş çapta desteklenmediğini unutmayın. Ayrıca Node.childNodes için her yöntemi bakın ?


1
Chrome 49 dönüşforEach in not a function
Vitaly Zdanevich

@VitalyZdanevich Deneyin Chromium 50
Oriol

Chrome 50'de alıyorumdocument.querySelectorAll(...).forEach is not a function
Vitaly Zdanevich

@VitalyZdanevich Chromium 50 üzerinde çalıştı ve hala Chromium 53 üzerinde çalışıyor. Belki Chrome 50'ye gönderilecek kadar kararlı kabul edilmedi.
Oriol


1

Bu daha güvenli bir yoldur:

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

0

getElementsByClassNameModern tarayıcılarda HTMLCollection döndürür .

hangi dizi benzeri argümanları benzer bir nesne tarafından iteratable olduğunu for...ofdöngü nedir aşağıya bakınız MDN doc bu konuda şöyle diyor:

İçin ... iterable nesneleri üzerinde deyimi bir döngü iterating yaratır ait olmak üzere,: yerleşik dize, Array, Array benzeri nesneler (örneğin, bağımsız değişkenler veya nodelist), TypedArray, Harita, Set, ve kullanıcı tanımlı Iterables. Nesnenin her farklı özelliğinin değeri için yürütülecek deyimlerle özel bir yineleme kancasını çağırır.

misal

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

Dizilişine göre öyle değil:error TS2488: Type 'HTMLCollectionOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.
Kaplumbağalar Sevimli

@TurtlesAreCute, İşte OP javascript değil daktilo kullanıyor ve ben daktiloda sorun için farklı bir çözüm olabilir bu yüzden vanilya js tavsiye göre cevap var.
Haritsinh Gohil

@TurtlesAreCute, Bu arada aynı zamanda daktiloda da çalışıyor, ancak belirli css sınıfının elemanını tutan değişkenin doğru tipinden bahsetmelisiniz, böylece buna göre dökülebilir, detay için bu cevaba bakınız .
Haritsinh Gohil

0

İşte jsperf üzerinde oluşturduğum bir test: https://jsperf.com/vanillajs-loop-through-elements-of-class

Chrome ve Firefox'taki en mükemmel sürüm, document.getElementsByClassName ile birlikte döngü için iyi bir eskidir:

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

Safari'de bu varyant kazanır:

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

Tüm tarayıcılar için en perfomant varyantını istiyorsanız, bu olabilir:

var elements = document.getElementsByClassName('testClass');
Array.from(elements).map(
    (element) => {
        return element.textContent = 'Tested';
    }
);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.