3

I'm using babel-polyfill and I'm trying to iterate an HTMLCollection object using for-of loop:

const elements = document.getElementsByClassName('some-class')
for (const element of elements) {
  console.log(element)
}

It's not working. I'm getting an error elements[Symbol.iterator] is not a function. How to make it work correctly?

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
thesublimeobject
  • 1,393
  • 1
  • 17
  • 22
  • To explain a few things that you misunderstood: core-js is a part of babel-polyfill, so there's no point in including it twice. If typing `Symbol.iterator` into console works, it only means that this symbol exists; it doesn't necessarily mean that `elements` have a `Symbol.iterator` property. for-of loop doesn't treat anything as an array—it simply invokes the `@@iterator` method of an object. – Michał Perłakowski Sep 29 '16 at 23:05
  • Also, in case you don't know what is an HTMLCollection object: it's an object returned by `document.getElementsByClassName()`. – Michał Perłakowski Sep 29 '16 at 23:10
  • @Gothdo, to clarify, I was not ever including both core-js and babel-polyfill: I had just tried importing them at different times in order to see if one or the other would work. Nonetheless, thank you for the clarification. – thesublimeobject Sep 29 '16 at 23:38
  • @thesublimeobject Having `Symbol.iterator` doesn't mean that `elements[Symbol.iterator]` should be there. Wether `elements` expose an _iterator_ interface (so `[Symbol.iterator]` method) is related to its type. It simply looks as in your case this type doesn't expose it – Mariusz Nowak Sep 30 '16 at 07:48

1 Answers1

4

From "Iterable DOM collections" on the core-js GitHub page:

Some DOM collections should have iterable interface or should be inherited from Array. That mean they should have keys, values, entries and @@iterator methods for iteration. So add them. Module web.dom.iterable:

{
  NodeList,
  DOMTokenList,
  MediaList,
  StyleSheetList,
  CSSRuleList
}
  #values()     -> iterator
  #keys()       -> iterator
  #entries()    -> iterator
  #@@iterator() -> iterator (values)

As you can see, that list doesn't include HTMLCollection. In order to be able to use for-of loop with HTMLCollection, you have to manually assign Array.prototype.values to HTMLCollection.prototype[Symbol.iterator]. See this example:

HTMLCollection.prototype[Symbol.iterator] = Array.prototype.values

for (const element of document.getElementsByTagName('a')) {
  console.log(element.href)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
<a href="//www.google.com">Google</a>
<a href="//www.github.com">GitHub</a>

Alternatively, you can just use document.querySelectorAll(), which a returns a NodeList object.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177