Working with lists of DOM elements in JavaScript

Posted . Visible to the public.

When you query the browser for DOM elements, there are some footguns you should know about.

Some lists are live

Some DOM APIs return live lists that automagically update their contents as the underlying DOM is manipulated.

Example

Let's assume we have two <div> elements:

<div id="one"></div>
<div id="two"></div>

We have multiple ways to retrieve a list of these elements. At first glance, they all look the same:

let liveList = element.getElementsByTagName('div')
let nonLiveList = document.querySelectorAll('div')

console.log(liveList)    // [#one, #two]
console.log(nonLiveList) // [#one, #two]

However, when we remove one element from the DOM, the live list reflects the change automatically:

document.querySelector('#two').remove()

console.log(liveList)    // [#one]
console.log(nonLiveList) // [#one, #two]

Snapshotting a live list

Lists that silently change their elements can be very surprising to work with.

If you cannot find a non-live version of that method, you can make a snapshot by copying it to an array:

let childrenSnapshot = [...element.children]

Returned lists are not arrays

When a DOM API returns a list, it is either a NodeList or an HTMLCollection. Both are array-like Show archive.org snapshot objects that only support basic iteration. However, only true arrays support the full range of methods like push(), map() or slice():

let htmlCollection = element.getElementsByTagName('div')
let nodeList = document.querySelectorAll('div')

for (let e of htmlCollection) { } // ✔ works
for (let e of nodeList) { }       // ✔ works

htmlCollection.map(...) // ✘ error
nodeList.map(...)       // ✘ error

Using Array methods on DOM lists

If you need to use an unsupported array method, you have three choices.

You can copy the DOM list into an array:

let array = [...nodeList]
array.map(...) // ✔ works

You can apply an Array method to the array-like object:

Array.prototype.map.call(nodeList, ...) // ✔ works

Or you might already use a utility package that works on array-like objects:

up.util.map(nodeList, ...)  // ✔ works

Some lists contain text nodes

Some API methods return lists that not only contain elements, but also text nodes or comment nodes.

For example, we have a <div> with a single child element:

<div id="container">
  <span>hello</span>
</div>

When we query that element for its children, some properties include text nodes:

container.children   // => [span]
container.childNodes // => [text, span, text]

In this example, the text nodes are the whitespace before and after the <span>.

When you want to exclude text nodes, you will usually find a different method in the API. E.g. element.nextSibling wg. element.nextElementSibling.

Most notably, querySelectorAll() will only select elements.

Profile picture of Henning Koch
Henning Koch
Last edit
Henning Koch
Keywords
javascript, script, js
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2025-11-26 10:17)