Joel Hassan

Arrays, NodeLists and HTML Element Collections

2019-06-26

nodelists

Introduction

Working with the DOM often involves dealing with lists of elements. The issue is that confusion can arise from working in an environment where the browser offers its own collection APIs on one hand, and JavaScript its own on the other. Errors often occur when attempting to call methods peculiar to JavaScript arrays on lists that are, in fact, collections offered by the browser.

This post seeks to provide an overview of the differences between these collection types, and some guidance on working with them.


Browser APIs vs JavaScript

Let's first shed light on what we mean by Application Programming Interfaces (APIs). What APIs do is abstract complex code away. They are constructs baked into programs that provide an easy way for you to create complex functionality, without exposing you to the hairiness of the complete code base.

This metaphor found on the MDN site is quite illustrative:

As a real-world example, think about the electricity supply in your house, apartment, or other dwellings. If you want to use an appliance in your house, you simply plug it into a plug socket and it works. You don't try to wire it directly into the power supply — to do so would be really inefficient and, if you are not an electrician, difficult and dangerous to attempt.

The point being: if you want to do something relatively complex, such as 3D graphics, you don't have to reinvent the wheel. Instead, you can leverage existing, ready-to-use APIs for your needs.

And that's what an API is in essence - an 'Interface' (a layer between two things) between a complete codebase and your program, enabling you to create stuff on top of existing functionality with ease.

In the context of browsers, an API refers to an interface for programming in the browser. These sit on top of JavaScript, which is built into the browser.

                                          +-------------------------------------+                             
                                          |                          BROWSER    |                             
                                          |     +--------------------------+    |                             
                                          |     |                          |    |                             
                                          |     |                          |    |                             
                                          |     |       BROWSER API        |    |                             
                                          |     |                          |    |                             
                                          |     |                          |    |                             
                                          |     ---------------------------|    |                             
                                          |     | JAVASCRIPT LANGUAGE CORE |    |                             
                                          |     |                          |    |                             
                                          |     +--------------------------+    |                             
                                          +-------------------------------------+                             

These browser APIs fall into two categories:

  1. Browser APIs. Example: Canvas API for drawing graphics using JavaScript
  2. Third-party APIs. Example: Twitter API that provides programmatic access to Twitter data

An array is part of the JavaScript API - a built-in object. NodeList and HTMLCollection, on the other hand, are web APIs belonging to the browser. They are included in the HTML specification rather than ECMAScript.

So methods such as querySelectorAll() and getElementsByTagName() aren’t actually JavaScript methods, they’re browser APIs that let you access DOM elements. You can then manipulate them with JavaScript, and since they're not part of JavaScript, you can manipulate them with other languages too, such as Python.

Why does this matter? It matters because the API for each collection type specifies the methods available to it and the type of data elements it can contain.

Let's have a look at the difference between them.


Collections

As for NodeList and HTMLCollection, both interfaces are lists of DOM nodes. HTMLCollection is the older one of the two, which the W3 docs describe as:

a historical artifact we cannot rid the web of.

It was used prior to the modern DOM, and is only included in the DOM specification to show how browsers used to work before it.

The two are essentially very similar, but differ in:

  1. the methods they provide -HTMLCollection provides the same methods as a NodeList and additionally a method called namedItem.
  2. the type of nodes they can contain. While a NodeList can contain any node type, an HTMLCollection is supposed to only contain Element nodes.

💡Note - Difference between a node and an element: A node refers to any DOM object (e.g. document node), whereas an element is a specific type of node (e.g. div node). As such, every element is a node, but not every node is an element.

Let's illustrate the differences using an example. Say we have a list with some list items:

    <ul>
          <!-- List items -->
          <li>List item 1</li>
          <li>List item 2</li>
          <li>List item 3</li>
          <li>List item 4</li>
          <li>List item 5</li>
     </ul>

We could do the following with JavaScript:

    ourList = document.querySelector('ul'); // retrieves our list 
    console.log(ourList.childNodes); // all of the list's children that are nodes
    console.log(ourList.children); // only the children that are elements

This is what the console would output for the code above:

consoleLog

The NodeList contains all of the unordered lists' children nodes. Since nodes are elements and more, this includes all the elements (in this case, list items only), whitespace and comments. The HTML collection only holds children elements.


Arrays

We've established that the two collections considered above are Web APIs. They are language agnostic, meaning that these APIs can be also used by languages other than JavaScript.

By contrasts, JavaScript arrays are objects that belong to the language. They are used to hold all sorts of things.

What's important to keep in mind is that these different types of objects (NodeLists and arrays) have their own methods and properties associated with them. That's the differentiator.

Returning to the above example, say we wanted to get a list of all the list items and we wrote:

    let ourList = document.querySelectorAll('li'); // NodeList stored in ourList variable

We now have a NodeList, but what if you have a need to use array methods such as push, pop, reduce or splice on the list - what to do?


Converting a NodeList to an Array

Before we consider conversion, you can test to see what type of list you're dealing with.

Determening collection type

    console.log(Array.isArray(ourList)); // false or true
    console.log(ourList.constructor.name); // NodeList (if NodeList)

A NodeList can be converted to an Array in a few ways.

We can use one of the following methods:

Array.from method

    ourList = Array.from(ourList) // returns an array from the list - simple, eh? 

Array.prototype.slice

    Array.prototype.slice.call(ourList) // returns a shallow copy of the origina in array form

ES6 style

If you are using ES6, you can use the spread operator

     ourList = [...ourList]; // I like this one
     

Keep in mind that there are other ways in which the NodeLists and arrays differ, namely whether the lists are live or static - live meaning that when elements are removed/added to the DOM, the list updates automatically. For a static list that is not the case. According to the docs:

A collection can be either live or static. Unless otherwise stated, a collection must be live.

As in, the default case is a live collection. Here are the different options from a SO answer:

   document.getElementsByClassName() retrieves a live HTMLCollection
    
    document.getElementsByTagName() also retrieves a live HTMLCollection
    
    document.getElementsByName() retrieves a live NodeList
    
    document.querySelectorAll() retrieves a static NodeList
    
    HTMLCollections appear to always be live

Conclusion

Distinguishing between browser APIs and JavaScript itself can be challenging initially. Poor/lacking documentation in some parts does not help either. However, I hope this post provided some amount of clarity on the topic.

If you'd like to read more into the different browser APIs, check out the links below:

WEB API - https://developer.mozilla.org/en-US/docs/Web/API

WEB APIs Reference - https://developer.mozilla.org/en-US/docs/Web/Reference/API

HTMLCollection

W3C DOM4