Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 331
Oriol Avatar asked Jan 20 '17 08:01

Oriol


People also ask

What is the use of symbol iterator in Javascript?

Description. Whenever an object needs to be iterated (such as at the beginning of a for...of loop), its @@iterator method is called with no arguments, and the returned iterator is used to obtain the values to be iterated. Some built-in types have a default iteration behavior, while other types (such as Object ) do not.

Is a NodeList iterable?

NodeList and DOMTokenList should have [Symbol. iterator] because they're iterable.

How do you find the length of a HTMLCollection?

The length() Property is used to return the collection of all HTML elements in a document. It is read-only property and is quite useful when a user wants to loop through an HTML Collection. Return Value: It returns a number which represent the number of all elements in a HTML Collection.


1 Answers

I found it, it's explained in WebIDL:

If the interface has any of the following:

  • an iterable declaration
  • an indexed property getter and an integer-typed attribute named “length”
  • a maplike declaration
  • a setlike declaration

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.

like image 161
Oriol Avatar answered Sep 21 '22 22:09

Oriol