forEach không phải là lỗi chức năng với mảng JavaScript


145

Tôi đang cố gắng thực hiện một vòng lặp đơn giản:

const parent = this.el.parentElement
console.log(parent.children)
parent.children.forEach(child => {
  console.log(child)
})

Nhưng tôi nhận được lỗi sau:

VM384: 53 Uncaught TypeError: Parent.children.forEach không phải là một chức năng

Mặc dù parent.childrennhật ký:

nhập mô tả hình ảnh ở đây

Điều gì có thể là vấn đề?

Lưu ý: Đây là một JSFiddle .


Vấn đề tương tự xảy ra với Element.siblings
Daut

@Daut có bởi vì Element.siblings trả về HTMLCollection và HTMLCollections không có phương thức forEach ()
Freddo

1
này bạn, người tìm kiếm google nếu bạn đang đọc kiểm tra hai lần này, hãy kiểm tra xem có phải là thủ đô E thay vì thuyết minh ....
Robert Sinclair

Câu trả lời:


127

Tùy chọn đầu tiên: gọi forEach một cách gián tiếp

Đây parent.childrenlà một mảng giống như đối tượng. Sử dụng giải pháp sau:

const parent = this.el.parentElement;

Array.prototype.forEach.call(parent.children, child => {
  console.log(child)
});

Các parent.childrenNodeListloại, mà là một mảng như đối tượng vì:

  • Nó chứa thuộc lengthtính, cho biết số lượng nút
  • Mỗi nút là một giá trị thuộc tính có tên số, bắt đầu từ 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

Xem thêm chi tiết trong bài viết này .


Tùy chọn thứ hai: sử dụng giao thức lặp

parent.childrenlà một HTMLCollection: thực hiện giao thức lặp . Trong môi trường ES2015, bạn có thể sử dụng HTMLCollectionvới bất kỳ cấu trúc nào chấp nhận lặp lại.

Sử dụng HTMLCollectionvới toán tử trải rộng:

const parent = this.el.parentElement;

[...parent.children].forEach(child => {
  console.log(child);
});

Hoặc với for..ofchu trình (là tùy chọn ưa thích của tôi):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}

Khi tôi sử dụng giải pháp của bạn, tôi không gặp vấn đề gì nữa, nhưng mã bên trong hàm ẩn danh không được thực thi. .so ..
Jérémy

Bạn sử dụng trình duyệt nào để Parent.children nói với bạn rằng đó là nodeList. Trên Firefox, nó cho tôi biết là HTMLCollection. Nếu đó là một nútList, .forEach () sẽ hoạt động
Freddo

104

parent.childrenkhông phải là một mảng. Đó là HTMLCollection và nó không có forEachphương thức. Bạn có thể chuyển đổi nó thành mảng đầu tiên. Ví dụ trong ES6:

Array.from(parent.children).forEach(child => {
    console.log(child)
});

hoặc sử dụng toán tử trải rộng:

[...parent.children].forEach(function (child) {
    console.log(child)
});

9
Tôi thích giải pháp này hơn nhiều so với việc làm rối với nguyên mẫu Array.
Daut

Và câu trả lời này là (một trong) câu trả lời đúng cho câu hỏi OP. Parent.children là một HTMLCollection không có phương thức .forEach
Freddo

18

parent.childrensẽ trả về một danh sách danh sách nút , về mặt kỹ thuật là Bộ sưu tập html . Đó là một mảng giống như đối tượng, nhưng không phải là một mảng, vì vậy bạn không thể gọi các hàm mảng trực tiếp trên nó. Ở bối cảnh này, bạn có thể sử dụng Array.from()để chuyển đổi nó thành một mảng thực sự,

Array.from(parent.children).forEach(child => {
  console.log(child)
})

Không, Parent.children không trả về nodeList mà là Bộ sưu tập HTML. Không giống nhau. Nếu đó là một nútList, .forEach sẽ hoạt động
Freddo

12

Một phiên bản ngây thơ hơn , ít nhất là bạn chắc chắn rằng nó sẽ hoạt động trên tất cả các thiết bị, không cần chuyển đổi và ES6:

const children = parent.children;
for (var i = 0; i < children.length; i++){
    console.log(children[i]);
}

https://jsfiddle.net/swb12kqn/5/


2
Được nâng cấp bởi vì tất cả các hàm ES6 mới này thực hiện chính xác cùng một thứ cũ tốt có sẵn, nhưng theo một cách lộn xộn, như mọi khi với JS
Freddo

8

parent.childrenlà một HTMLCollectionđối tượng giống như mảng. Đầu tiên, bạn phải chuyển đổi nó thành một phương thức thực Arrayđể sử dụng Array.prototype.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})

2
Hoặc không chuyển đổi nó, nhưng sử dụng .call () trên .forEach ()?
nnnnnn

@nnnnnn Xem câu trả lời của tôi dưới đây.
Dmitri Pavlutin

Có nhiều cách để chuyển đổi đối tượng giống như mảng thành Mảng :) Đây là một trong số đó
Dmitriy

@DmitriyLoskutov Bạn không cần phải chuyển đổi nó - JavaScript là ngôn ngữ gõ vịt. Chỉ cần sử dụng tính năng này.
Dmitri Pavlutin

5

Đó là bởi vì parent.childrenlà một NodeList và nó không hỗ trợ .forEachphương thức (vì NodeList là một mảng giống như cấu trúc nhưng không phải là một mảng), vì vậy hãy thử gọi nó bằng cách trước tiên chuyển đổi nó thành mảng bằng cách sử dụng

var children = [].slice.call(parent.children);
children.forEach(yourFunc);

Không, nó không phải là NodeList, nó là Bộ sưu tập HTML
Freddo

5

Không cần choforEach , bạn chỉ có thể lặp lại bằng cách sử dụng fromtham số thứ hai, như vậy:

let nodeList = [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]
Array.from(nodeList, child => {
  console.log(child)
});


Tin buồn là Parent.children không phải là một nútList ... .from () sẽ không hoạt động.
Freddo

@Cedric nếu đối tượng của bạn không phải là NodeList, thì bạn nên hỏi một câu hỏi mới cụ thể để giải quyết nó. Ở đây, việc bỏ phiếu được sử dụng khi câu trả lời về bản chất là sai hoặc có hại và, như bạn có thể thấy bởi đoạn mã, tất cả các yếu tố của đối tượng được lặp lại và in, đó là mục tiêu của câu hỏi của OP.
Armfoot

Đúng, vấn đề là câu hỏi của OP liên quan đến Bộ sưu tập HTML, không phải là nút Danh sách ... Vì vậy, câu trả lời chỉ đơn giản là không trả lời câu hỏi
Freddo

@Cedric câu trả lời này cũng sẽ lặp lại trên Bộ sưu tập HTML vì Array.fromchuyển đổi đối tượng được cung cấp trong tham số đầu tiên thành một mảng. Kết quả giống như trong câu trả lời của madox2 mà không cần thêm forEachvòng lặp ( tài liệu Array.fromMDN ).
Armfoot

4

Nếu bạn đang cố gắng lặp lại NodeListnhư thế này:

const allParagraphs = document.querySelectorAll("p");

Tôi rất khuyên bạn nên lặp nó theo cách này:

Array.prototype.forEach.call(allParagraphs , function(el) {
    // Write your code here
})

Cá nhân tôi đã thử một vài cách nhưng hầu hết chúng đều không hoạt động vì tôi muốn lặp lại NodeList , nhưng cách này hoạt động như một bùa mê, hãy thử xem!

Các NodeListkhông phải là một mảng, nhưng chúng tôi đối xử với nó như một mảng, sử dụngArray. Vì vậy, bạn cần phải biết rằng nó không được hỗ trợ trong các trình duyệt cũ!

Cần thêm thông tin về NodeList? Vui lòng đọc tài liệu của nó trên MDN .


1
Câu trả lời này rõ ràng hoạt động trên nodeList. Vấn đề là Parent.children trả về Bộ sưu tập HTML, không phải là một danh sách nút ...
Freddo

3

Vì bạn đang sử dụng các tính năng của ES6 (các hàm mũi tên ), bạn cũng có thể chỉ cần sử dụng một vòng lặp for như thế này:

for(let child of [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]) {
  console.log(child)
}


Nâng cao. Thật là một sự mâu thuẫn, cú pháp ES6, mặc dù ... Khiến tôi muốn khóc, và tôi đến từ nền tảng C ++ ...
Freddo

1

Bạn có thể kiểm tra xem bạn đã gõ forEach đúng chưa, nếu bạn gõ foreach như trong các ngôn ngữ lập trình khác thì nó sẽ không hoạt động.


0

Bạn có thể sử dụng childNodesthay vì children,childNodes cũng đáng tin cậy hơn khi xem xét các vấn đề tương thích trình duyệt, thêm thông tin ở đây :

parent.childNodes.forEach(function (child) {
    console.log(child)
});

hoặc sử dụng toán tử trải rộng:

[...parent.children].forEach(function (child) {
    console.log(child)
});
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.