Menggunakan forEach pada array dari getElementsByClassName menghasilkan "TypeError: undefined bukanlah sebuah fungsi"

92

Di JSFiddle saya , saya hanya mencoba melakukan iterasi pada berbagai elemen. Array tidak kosong, seperti yang dibuktikan oleh pernyataan log. Namun panggilan untuk forEachmemberi saya kesalahan (tidak terlalu membantu) "Tidak tertangkap TypeError: undefinedbukan fungsi".

Saya pasti melakukan sesuatu yang bodoh; apa yang saya lakukan salah?

Kode saya:

var arr = document.getElementsByClassName('myClass');
console.log(arr);
console.log(arr[0]);
arr.forEach(function(v, i, a) {
  console.log(v);
});
.myClass {
  background-color: #FF0000;
}
<div class="myClass">Hello</div>

Jer
sumber
8
arrbukan sebuah array, tapi a HTMLCollection. Itu tidak memiliki metode yang sama dengan array. developer.mozilla.org/en-US/docs/Web/API/… . Berikut adalah posting SO tentang itu bahkan: stackoverflow.com/questions/13433799/…
Ian
Sesuatu seperti [1,2,3].forEach(function(v,i,a) { console.log(v); });itu baik-baik saja. Apa perbedaan antara this dan array dalam contoh saya?
Yer
Anda tidak memiliki array dalam contoh Anda. Apa yang membuat Anda berpikir itu sebuah array?
Ian
3
@Jer: Karena arr instanceof Arrayakan mengakibatkan falsetidak dapat memanfaatkan metode prototipe apa pun dari Arrayobjek seperti Array.prototype.forEach () . arradalah HTMLCollection dan array seperti objek (tetapi tidak mewarisi dari atau instantiate Array). Oleh karena itu forloop standar Anda akan bekerja karena hanya melakukan iterasi melalui indeks objek dan bukan merupakan prototipe Array.
Tidak
1
@ Jer — Anda harus melihat perbedaan antara objek bawaan dan objek host. Yang pertama sesuai dengan ECMA-262, yang kemudian hanya sesuai keinginan tuan rumah. DOM memiliki banyak objek yang memungkinkan akses ke anggota dengan indeks (document.images, document.forms, form.elements, select.options, dll.), Sebagian besar didasarkan pada antarmuka NodeList .
RobG

Jawaban:

164

Itu karena document.getElementsByClassNamemengembalikan HTMLCollection , bukan array.

Untungnya, ini adalah objek "mirip-larik" (yang menjelaskan mengapa itu dicatat seolah-olah itu adalah objek dan mengapa Anda dapat melakukan iterasi dengan forloop standar ), jadi Anda dapat melakukan ini:

[].forEach.call(document.getElementsByClassName('myClass'), function(v,i,a) {

Dengan ES6 (di browser modern atau dengan Babel), Anda juga dapat menggunakan Array.fromarray yang membangun dari objek seperti array:

Array.from(document.getElementsByClassName('myClass')).forEach(v=>{

atau sebarkan objek seperti larik ke dalam larik:

[...document.getElementsByClassName('myClass'))].forEach(v=>{
Denys Séguret
sumber
2
@Jer argumentsadalah satu. Objek jQuery adalah hal lain. Anda dapat membuatnya sendiri:var a = {"0": "str1", "1": "str2", length: 2}
Ian
1
Di sini kita pergi dengan browser lama lagi… melewatkan objek host ke metode asli akan gagal di IE 8 dan yang lebih rendah. Mungkin tidak ada yang peduli, tapi beberapa mungkin. ;-) Oh, ini juga tidak mendukung getElementsByClassName , tetapi querySelectorAll('.myClass')seharusnya berfungsi. Saya masih menunggu iterator ditambahkan ke NodeList API. :-(
RobG
2
@Jer: Sebagai catatan tambahan jika Anda berniat untuk keluar dari loop karena alasan apapun Array.prototype.forEachtidak akan membiarkan Anda melakukan itu. Jika Anda memiliki persyaratan itu nanti gunakan forloop standar atau jika Anda ingin menggunakan objek array gunakan Array.prototype.everyatau Array.prototype.some(perhatikan bahwa setiap / beberapa tidak didukung di IE8 atau kurang)
Tidak
1
@Ian Anda memerlukan sambungan agar objek menjadi "seperti array". Bandingkan log di sini: jsbin.com/sigut/1/edit
Denys Séguret
1
@Ian TBH definisi "seperti array" sangat kabur dan tergantung pada penggunaannya. Kadang-kadang saya tidak termasuk splicedalam definisi itu, tetapi ketika saya ingin lebih "array-seperti" untuk dapat menggunakan map, filterdan sebagainya, maka saya memasukkannya. Penggunaan iterasi sederhana forEachtidak perlu splice.
Denys Séguret
11

Coba ini seharusnya berhasil:

<html>
  <head>
    <style type="text/css">
    </style>
  </head>
  <body>
   <div class="myClass">Hello</div>
   <div class="myClass">Hello</div>

<script type="text/javascript">
    var arr = document.getElementsByClassName('myClass');
    console.log(arr);
    console.log(arr[0]);
    arr = [].slice.call(arr); //I have converted the HTML Collection an array
    arr.forEach(function(v,i,a) {
        console.log(v);
    });
</script>


<style type="text/css">
    .myClass {
    background-color: #FF0000;
}
</style>

  </body>
</html>
Vaibhav Jain
sumber
0

Jika Anda ingin mengakses ID dari setiap elemen dari kelas tertentu, Anda dapat melakukan hal berikut:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });
Nelles
sumber