Can HTMLCollections be iterated with for...of (Symbol.iterator)?
Asked Answered
R

2

10

DOM4 makes NodeLists iterable:

interface NodeList {
  getter Node? item(unsigned long index);
  readonly attribute unsigned long length;
  iterable<Node>;
};

According to WebIDL, this means

Objects implementing an interface that is declared to be iterable support being iterated over to obtain a sequence of values.

Note: In the ECMAScript language binding, an interface that is iterable will have “entries”, “forEach”, “keys”, “values” and @@iterator properties on its interface prototype object.

So the following is possible:

for (var el of document.querySelectorAll(selector)) ...

I noticed the same seems to work for HTMLCollections, both on Firefox and Chrome:

for (var el of document.getElementsByTagName(tag)) ...

In fact, I get

HTMLCollection.prototype[Symbol.iterator] === [][Symbol.iterator]; // true

However, HTMLCollection is not defined as iterable:

interface HTMLCollection {
  readonly attribute unsigned long length;
  getter Element? item(unsigned long index);
  getter Element? namedItem(DOMString name);
};

I also checked the WHATWG DOM spec and it's not iterable neither.

Then, is this behavior standard or not? Is HTMLCollection supposed to have an @@iterator in the prototype?

Repository answered 20/1, 2017 at 8:18 Comment(1)
R
8

I found it, it's explained in WebIDL:

If the interface has any of the following:

then a property must exist whose name is the @@iterator symbol, with attributes { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true } and whose value is a function object. [...]

If the interface defines an indexed property getter, then the Function object is %ArrayProto_values%.

In this case, HTMLCollections have an indexed property getter:

getter Element? item(unsigned long index);

and an integer-typed attribute named “length”:

readonly attribute unsigned long length;

Therefore, yes, it's supposed to work. In fact, it would also work for NodeLists even if they weren't declared as iterable, but then they wouldn't have the entries, forEach, keys and values properties. As @lonesomeday mentions, it's probable that HTMLCollection is not defined as iterable because adding these methods wouldn't be backwards-compatible, due to the fact that the namedItem getter accepts arbitrary strings.

Repository answered 20/1, 2017 at 8:52 Comment(2)
I'm going to guess it's because of either the namedItem method on an HTMLCollection or the statement "Note: Elements is the better solution for representing a collection of elements. HTMLCollection is an historical artifact we cannot rid the web of."Risteau
@Risteau Yes, it seems so, since namedItem accepts a string, adding properties like forEach could break things.Repository
D
0

In JavaScript practically everything that has a structure of an iterable can go through some iteration engine operations such as : for..of and ...spread

Anything is iterable if it returns an iterator for [[Get]] operations against a property that it stored @@iterator symbol which in that case apparently HTMLCollection returns such an object.

An iterator is counted per se if it has a : next() {method}:, and two other optional methods as well that are

return() {method}: stops iterator and returns IteratorResult
throw() {method}: signals error and returns IteratorResult

This is an example of a complete custom object that is iterable.

    const calculations = {
      counting: 1,
      [Symbol.iterator]: function(){ return this; },
      next: function(){

        if(this.counting <= 3){

          return {
            value: this.counting++,
            done: false
          }
        }

        return { value: this.counting, done: true}
      }
    };

const data = ...calculations; // [1,2,3]

So in your case, as long as HTMLCollection returns a proper iterator you can apply for..of and ...spread, but if you concern is wether this meets the specification than I have to tell you that I don't have a bachelor computer science degree yet :P

Dessiedessma answered 20/1, 2017 at 8:37 Comment(2)
Thanks, I already knew about @@iterator, my concern is whether HTMLCollection is supposed to return a proper iteratorRepository
@Repository I think this depends on the implementation. HTMLCollection interface has no declaration for an iterator but when implemented you can include another interface as well. In this case HTMLCollection and NodeList are a representation of collections that sure enough is iterable.Dessiedessma

© 2022 - 2024 — McMap. All rights reserved.