A collection is not an array

    James Edwards
    Share

    I’m occassionally irked by the fact that a collection of DOM elements (more formally called a NodeList) can’t be manipulated like an array, because it isn’t one. However it does look like one, and thinking it is one is a mistake made so often by JavaScript novices that for our upcoming JavaScript Reference I felt it necessary to note this point for every single DOM object that is, or returns, a collection.

    You can iterate through a collection like an array:

    for(var i=0; i<collection.length; i++)
    {
    	//whatever
    }

    But you can’t use Array methods like push(), splice() or reverse() to manipulate it.

    Except that you can, if you take the next step and convert it into an array. This is in fact trivial:

    function collectionToArray(collection)
    {
    	var ary = [];
    	for(var i=0, len = collection.length; i < len; i++)
    	{
    		ary.push(collection[i]);
    	}
    	return ary;
    }

    The code above is fully cross-browser, and is called with the original collection as an argument:

    var elements = collectionToArray(document.getElementsByTagName('*'));

    However if you only need to deal with browsers that support native object prototyping (Opera, Firefox and Safari 3) then you can simply create a toArray() method of NodeList:

    NodeList.prototype.toArray = function()
    {
    	var ary = [];
    	for(var i=0, len = this.length; i < len; i++)
    	{
    		ary.push(this[i]);
    	}
    	return ary;
    };

    Which can then be called as a method of the individual collection:

    var elements = document.getElementsByTagName('*').toArray();

    There is one obvious disadvantage to this conversion (however it’s done), which is that the resulting array will no longer be a NodeList. Obvious, yes, but relevant because it has two implications:

    • It will lose the properties and methods it inherited from NodeList. However NodeList only has one property (its length, which is also available for an array), and one method (the item() method, which is usually redundent anyway, since members can still be referred to with square-bracket notation). So this loss is not at all significant
    • It will no longer be a live collection. A NodeList is a reference to a collection of objects, and if that collection changes (for example, elements are added or removed) the NodeList will automatically update to reflect that change; conversely our array is a static snapshot of the collection at one point in time, and so won’t update in response to changes in the DOM. Depending on your application, that could be significant.