Web Development Pitfall No.1: Confusing a DOM collection with a JS array

gwagner's picture

Summary

Don't confuse a DOM collection with a JS array: Array functions, such as the forEach looping method, cannot be applied to a DOM collection!

For instance, when you retrieve all rows of an HTML table element, you get an HTMLCollection, which is an array-like object, but not an instance of Array, and therefore the following code does not succeed because the Array method forEach is not defined for an HTMLCollection like myTableEl.rows:

var myTableEl = document.getElementById("myTableEl");
myTableEl.rows.forEach( function (row) {
  ... // process row
})

There are two solutions how to loop over a DOM collection like myTableEl.rows. Either by using an ordinary for loop, like so:

var myTableEl = document.getElementById("myTableEl");
var i=0, row=null;
for (i=0; i < myTableEl.rows.length; i++) {
  row = myTableEl.rows[i];
  ... // process row
}

or by invoking the Array method forEach on the DOM collection with the help of call in the following way:

var myTableEl = document.getElementById("myTableEl");
Array.prototype.forEach.call( myTableEl.rows, function (row) {
  ... // process row
})

More on DOM Collections

Mozilla provides a pretty good overview of DOM interfaces, though not all methods have proper documentation yet. The ultimate reference is, of course, the hard-to-digest DOM4 specification, which comes in two forms: the W3C DOM4 spec and the  WHATWG's DOM Living Standard.

A DOM collection is an array-like object coll, the items of which can be accessed with the array index notation coll[i], and that has a length attribute such that a for loop can be used for iterating over it. There are 4 different types of DOM collections:

  1. The most important one, HTMLCollection, represents a collection of HTML elements, typically obtained by retrieving HTML elements with one of the methods getElementsByTagName, getElementsByClassName or querySelectorAll.
  2. A DOMTokenList represents a collection of items of an HTML attribute value list, such as the values of the HTML class attribute.
  3. A NamedNodeMap represents a collection of attributes (notice that, despite the historical name of this DOM collection type, attributes are no longer considered to be nodes in DOM4).
  4. A NodeList represents a collection of DOM nodes, which may be elements, plain text, comments or processing instructions.

All of these DOM collections have the item method in common, which implies that their items can be accessed with the array index notation. Two of them, HTMLCollection and NamedNodeMap, also have a (get)namedItem method, which implies that their items can be accessd with the key-value map notation. In the case of an HTMLCollection, the keys are provided by the elements' id attribute, while in the case of a NamedNodeMap, the keys are provided by the attribute names.

This is summarized in the following UML class diagram.

Notice that a DOM element is a special type of DOM node (as expressed by the UML generalization arrow), so we can also use the attribute childNodes for an HTML element or SVG element for collecting all child nodes of it in the form of a NodeList, including child elements, but also comments, processing instructions, etc. However, when you only need to retrieve child elements, such as all child elements of a certain div, then you better use the method children, whcih returns an HTMLCollection.

Category: