In short, because the specification says so. For getElementsByName
:
The getElementsByName(name)
method takes a string name, and must return a live NodeList containing all the HTML elements in that document that have a name attribute whose value is equal to the name argument (in a case-sensitive manner), in tree order. When the method is invoked on a Document object again with the same argument, the user agent may return the same as the object returned by the earlier call. In other cases, a new NodeList object must be returned.
For getElementsByClassName
:
The getElementsByClassName(classNames)
method, when invoked, must return the list of elements with class names classNames for this
.
(where this
is usually document
), and where the "list of elements" is constructed by:
The list of elements with class names classNames for a node root is the HTMLCollection returned by the following algorithm.....
Similarly, querySelectorAll
returns a static NodeList.
Interestingly enough, the W3Schools docs are wrong
Not surprising - w3schools is notoriously not very reliable. Better to reference MDN or official specifications.
It's important to keep in mind the difference between the collections. On recent browsers, like you've encountered, there exists a NodeList.prototype.forEach
method. No such method exists for HTMLCollections. Some of the collections are static, while some are live. The live collections (like with getElementsByClassName
) can mutate themselves while you're iterating over them (unlike static collections and arrays), but the static collections (like with querySelectorAll
) won't mutate themselves.
But, note that although only NodeLists have a forEach
method, both collections have a Symbol.iterator
property which you can iterate over with for..of
:
for (const elm of document.getElementsByName('foo')) {
// ...
}
for (const elm of document.getElementsByClassName('foo')) {
// ...
}
console.log('No errors');