Static vs live NodeLists

The Document.querySelectorAll() method and the Node.childNodes property both return a NodeList. But the former returns a static NodeList, while the latter returns a live NodeList. This is an important difference to understand, else your code might behave in unexpected ways.

Static NodeLists

Static NodeLists are unaffected by future changes to the DOM.

Imagine the following HTML…

<body>

  <h1>This is a heading.</h1>

  <p>This is a paragraph.</p>
  <p>This is another paragraph.</p>
  <p>This is yet another paragraph.</p>

</body>

…then consider the following JavaScript:

// Select all paragraphs inside the body
var paragraphs = document.body.querySelectorAll("p");

// Log the number of paragraphs inside the NodeList (3)
console.log(paragraphs.length);

This logs 3, which is what we’d expect, since there are three paragraph elements inside the NodeList.

Look what happens if we add another paragraph to the <body>:

// Create a new paragraph
var newParagraph = document.createElement("p");

// Add the new paragraph to the body
document.body.appendChild(newParagraph);

// Log the number of paragraphs (3)
console.log(paragraphs.length);

Although there are now four paragraphs inside the <body> element, the NodeList still contains only three. This is because static NodeLists do not pick up further changes to the DOM.

Live NodeLists

Live NodeLists are affected by further changes to the DOM.

Assume the same HTML as above:

<body>

  <h1>This is a heading.</h1>

  <p>This is a paragraph.</p>
  <p>This is another paragraph.</p>
  <p>This is yet another paragraph.</p>

</body>

Let’s save the <body> element’s childNodes property to a variable, then log its length:

// Get all child nodes inside the body
var childNodes = document.body.childNodes;

// Log the number of child nodes (9)
console.log(childNodes.length);

This returns 9 since there are four element nodes plus five whitespace nodes.

(The childNodes property considers all types of node including whitespace, comments, etc—not just HTML elements.)

But look what happens this time if we add another element:

// Create a new paragraph
var newParagraph = document.createElement("p");

// Add the new paragraph to the body
document.body.appendChild(newParagraph);

// Log the number of child nodes (10)
console.log(childNodes.length);

There are now ten child nodes inside the <body> element.

Because the childNodes property returns a live NodeList, it gets updated automatically.

Even though I didn’t manually update the childNodes variable, its length property shows the updated number, which is 10.

In practical terms

As they explain in the MDN Web Docs:

It's good to keep this distinction in mind when you choose how to iterate over the items in the NodeList, and whether you should cache the list's length.


If you have questions, feedback, or any other suggestions, please do email me. I'd love to hear from you!