Sử dụng forEach trên một mảng từ getElementsByClassName dẫn đến "TypeError: undefined không phải là một hàm"


91

Trong JSFiddle của tôi , tôi chỉ đơn giản là đang cố gắng lặp lại một mảng các phần tử. Mảng không trống, như các câu lệnh nhật ký chứng minh. Tuy nhiên, lệnh gọi forEachcho tôi (không hữu ích lắm) "Uncaught TypeError: undefinedkhông phải là một chức năng".

Tôi phải làm điều gì đó ngu ngốc; tôi đang làm gì sai

Mã của tôi:

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>


8
arrkhông phải là một mảng, mà là một HTMLCollection. Nó không có các phương thức giống như một mảng. developer.mozilla.org/en-US/docs/Web/API/… . Đây là một bài đăng SO về nó ngay cả: stackoverflow.com/questions/13433799/…
Ian

Một cái gì đó như [1,2,3].forEach(function(v,i,a) { console.log(v); });là tốt. Sự khác biệt giữa mảng này và mảng trong ví dụ của tôi là gì?
Jer

Bạn không có một mảng trong ví dụ của mình. Điều gì khiến bạn nghĩ đó là một mảng?
Ian

3
@Jer: Như arr instanceof Arraysẽ dẫn đến falsenó không có thể tận dụng bất kỳ phương pháp nguyên mẫu của các Arrayđối tượng như Array.prototype.forEach () . arrlà một tập hợp HTMLC và một mảng giống như đối tượng (nhưng không kế thừa từ hoặc khởi tạo Array). Do đó, forvòng lặp tiêu chuẩn của bạn sẽ hoạt động vì nó chỉ lặp lại qua chỉ mục của đối tượng và không phải là nguyên mẫu của Array.
Không,

1
@ Jer — bạn nên xem xét sự khác biệt giữa các đối tượng tích hợp và máy chủ. Cái trước phù hợp với ECMA-262, cái sau chỉ theo ý muốn của chủ nhà. DOM có nhiều đối tượng cho phép truy cập vào các thành viên theo chỉ mục (document.images, document.forms, form.elements, select.options, v.v.), chủ yếu dựa trên giao diện NodeList .
RobG

Câu trả lời:


162

Đó là bởi vì document.getElementsByClassNametrả về một HTMLCollection , không phải một mảng.

May mắn thay, đó là một đối tượng "giống mảng" (điều này giải thích tại sao nó được ghi lại như thể nó là một đối tượng và tại sao bạn có thể lặp lại với một forvòng lặp chuẩn ), vì vậy bạn có thể làm điều này:

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

Với ES6 (trên các trình duyệt hiện đại hoặc với Babel), bạn cũng có thể sử dụng cách Array.fromxây dựng mảng từ các đối tượng giống mảng:

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

hoặc trải đối tượng dạng mảng vào một mảng:

[...document.getElementsByClassName('myClass'))].forEach(v=>{

2
@Jer argumentslà một. Các đối tượng jQuery là một đối tượng khác. Bạn có thể tự làm một cái:var a = {"0": "str1", "1": "str2", length: 2}
Ian

1
Ở đây chúng ta lại làm với các trình duyệt cũ… việc chuyển một đối tượng máy chủ sang một phương thức gốc sẽ không thành công trong IE 8 trở xuống. Có thể không ai quan tâm, nhưng một số có thể. ;-) Ồ, nó cũng không hỗ trợ getElementsByClassName , nhưng querySelectorAll('.myClass')sẽ hoạt động. Tôi vẫn đang đợi các trình vòng lặp được thêm vào API NodeList. :-(
RobG

2
@Jer: Như một lưu ý phụ nếu bạn định thoát ra khỏi vòng lặp vì bất kỳ lý do gì Array.prototype.forEachsẽ không cho phép bạn làm điều đó. Nếu bạn có yêu cầu rằng sau sử dụng tiêu chuẩn forvòng lặp hoặc nếu bạn muốn sử dụng việc sử dụng đối tượng mảng Array.prototype.everyhoặc Array.prototype.some(lưu ý rằng mặc dù mỗi / số không được hỗ trợ trong IE8 hoặc ít hơn)
Nope

1
@Ian Bạn cần mối nối để đối tượng có dạng "mảng". Hãy so sánh các bản ghi ở đây: jsbin.com/sigut/1/edit
Denys SEGURET

1
@Ian TBH định nghĩa về "array-like" rất mờ nhạt và phụ thuộc vào việc sử dụng. Đôi khi tôi không đưa splicevào định nghĩa đó nhưng khi tôi muốn "giống mảng" hơn để có thể sử dụng map, filterv.v., thì tôi đưa nó vào. Lặp lại đơn giản bằng cách sử dụng forEachkhông cần splice.
Denys Séguret

11

Hãy thử nó sẽ hoạt động:

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

0

trong trường hợp bạn muốn truy cập ID của từng phần tử của một lớp cụ thể, bạn có thể làm như sau:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.