การใช้ forEach บนอาร์เรย์จาก getElementsByClassName ส่งผลให้ "TypeError: undefined ไม่ใช่ฟังก์ชัน"


92

ในJSFiddle ของฉันฉันแค่พยายามวนซ้ำองค์ประกอบต่างๆ อาร์เรย์ไม่ว่างเปล่าตามที่ข้อความบันทึกพิสูจน์ แต่การโทรแจ้งข้อผิดพลาดforEach"Uncaught TypeError: undefinedis not a function" ให้กับฉัน

ฉันต้องทำอะไรโง่ ๆ ผมทำอะไรผิดหรือเปล่า?

รหัสของฉัน:

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
arrไม่ใช่อาร์เรย์ แต่เป็นHTMLCollection. ไม่มีวิธีการเดียวกันกับอาร์เรย์ developer.mozilla.org/en-US/docs/Web/API/… . นี่คือโพสต์ SO เกี่ยวกับเรื่องนี้: stackoverflow.com/questions/13433799/…
เอียน

บางอย่าง[1,2,3].forEach(function(v,i,a) { console.log(v); });ก็สบายดี อะไรคือความแตกต่างระหว่างสิ่งนี้กับอาร์เรย์ในตัวอย่างของฉัน
เจอร์

คุณไม่ได้มีอาร์เรย์ในตัวอย่างของคุณ อะไรทำให้คุณคิดว่ามันเป็นอาร์เรย์
Ian

3
@Jer: ในฐานะที่arr instanceof Arrayจะส่งผลให้falseไม่สามารถใช้ประโยชน์จากวิธีการต้นแบบใด ๆ ของArrayวัตถุเช่นArray.prototype.forEach () arrเป็นHTMLCollectionและอาร์เรย์เหมือนออบเจ็กต์ (แต่ไม่สืบทอดจากหรือสร้างอินสแตนซ์Array) ดังนั้นมาตรฐานของforวงจะทำงานตามที่เพียง iterates Arrayผ่านดัชนีของวัตถุและไม่ได้เป็นต้นแบบของ
Nope

1
@ เจอ - คุณควรพิจารณาความแตกต่างระหว่างออบเจ็กต์ในตัวและโฮสต์ อดีตเป็นไปตาม ECMA-262 ซึ่งภายหลังเป็นไปตามที่โฮสต์ต้องการเท่านั้น พระมีวัตถุมากมายที่ช่วยให้การเข้าถึงให้กับสมาชิกโดยดัชนี (document.images, document.forms, form.elements, select.options ฯลฯ ) บนพื้นฐานส่วนใหญ่NodeListอินเตอร์เฟซ
RobG

คำตอบ:


164

นั่นเป็นเพราะ document.getElementsByClassNameส่งคืนHTMLCollectionไม่ใช่อาร์เรย์

โชคดีที่มันเป็นอ็อบเจ็กต์ "คล้ายอาร์เรย์" (ซึ่งอธิบายได้ว่าทำไมมันถึงถูกบันทึกราวกับว่ามันเป็นอ็อบเจ็กต์และทำไมคุณถึงวนซ้ำด้วยforลูปมาตรฐาน) คุณจึงสามารถทำได้:

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

ด้วย ES6 (บนเบราว์เซอร์รุ่นใหม่หรือ Babel) คุณยังสามารถใช้Array.fromซึ่งสร้างอาร์เรย์จากวัตถุที่มีลักษณะคล้ายอาร์เรย์:

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

หรือแพร่กระจายวัตถุคล้ายอาร์เรย์ไปยังอาร์เรย์:

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

2
@ เจ๊argumentsเป็นหนึ่ง. วัตถุ jQuery เป็นอีกอย่างหนึ่ง คุณสามารถสร้างเองได้:var a = {"0": "str1", "1": "str2", length: 2}
Ian

1
ที่นี่เราจะไปกับเบราว์เซอร์เก่าอีกครั้ง ... การส่งผ่านวัตถุโฮสต์ไปยังวิธีดั้งเดิมจะล้มเหลวใน IE 8 และต่ำกว่า อาจไม่มีใครสนใจ แต่บางคนอาจ ;-) โอ้มันไม่รองรับgetElementsByClassNameเหมือนกัน แต่querySelectorAll('.myClass')ควรใช้งานได้ ฉันยังคงรอให้มีการเพิ่มตัวทำซ้ำใน NodeList API :-(
RobG

2
@Jer: ในฐานะที่เป็นข้อสังเกตหากคุณตั้งใจที่จะแยกออกจากวงไม่ว่าด้วยเหตุผลใดก็ตามArray.prototype.forEachจะไม่ยอมให้คุณทำเช่นนั้น หากคุณมีข้อกำหนดนั้นให้ใช้forลูปมาตรฐานในภายหลังหรือหากคุณต้องการใช้อ็อบเจ็กต์อาร์เรย์ให้ใช้Array.prototype.everyหรือArray.prototype.some(โปรดทราบว่าทุก / บางส่วนไม่รองรับใน IE8 หรือน้อยกว่า)
Nope

1
@Ian คุณต้องประกบเพื่อให้วัตถุเป็น "เหมือนอาร์เรย์" เปรียบเทียบบันทึกที่นี่: jsbin.com/sigut/1/edit
Denys Séguret

1
@Ian TBH คำจำกัดความของ "array-like" นั้นคลุมเครือมากและขึ้นอยู่กับการใช้งาน บางครั้งผมไม่ได้รวมspliceอยู่ในคำนิยามว่า แต่เมื่อฉันต้องการที่จะมีมากขึ้น "อาร์เรย์เหมือน" เพื่อให้สามารถใช้งานmap, filterและอื่น ๆ แล้วผมรวมไว้ ย้ำง่ายโดยใช้ไม่จำเป็นต้องforEach splice
Denys Séguret

11

ลองสิ่งนี้ควรได้ผล:

<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

ในกรณีที่คุณต้องการเข้าถึง ID ของแต่ละองค์ประกอบของคลาสเฉพาะคุณสามารถทำสิ่งต่อไปนี้:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.