Introduction
When working with the DOM in JavaScript, you’ll often retrieve groups of elements or nodes. Two of the most common of them you’ll meet are HTMLCollection and NodeList. They look similar at first glance — both are array-like — but they behave differently in important ways.
Neither is a true JavaScript Array, so they don’t have all the usual array methods. Knowing exactly what each one is, how to get it, and where they differ will save you hours of debugging. This short guide covers everything you need.
What is an HTMLCollection?
An HTMLCollection is a live collection that contains only elements nodes (no content nodes, no comments nodes etc.).
It is not an array. You can access items by index or name, but it has no modern array methods like forEach, map, filter, etc.
How to get an HTMLCollection
document.getElementsByTagName('p')
document.getElementsByClassName('highlight')
element.children
document.forms
document.images
// ...and several other legacy properties
Key point: Because it is live, the collection updates automatically whenever the DOM changes (elements added, removed, or moved).
What is a NodeList?
A NodeList is a collection that can contain any type of node (elements, text nodes, comments, etc.). Some NodeLists are static and some are live.
It is also not an array, but modern NodeLists are friendlier: they support forEach(), for...of, entries(), keys(), and values().
How to get a NodeList
document.querySelectorAll('p') // ← static
element.childNodes // ← live
document.getElementsByName('username') // live in some cases
Key point: Most people use querySelectorAll(), which returns a static snapshot. Other NodeLists (like childNodes) are live.
Key Differences: HTMLCollection vs NodeList
forEach() Support
NodeList has forEach().
HTMLCollection does not.
If you try:
const collection = document.getElementsByTagName('li');
collection.forEach(...) // → TypeError: collection.forEach is not a function
Fix: Convert to a real array first:
Array.from(collection).forEach(...)
Live vs Static Behavior
HTMLCollection is always live.
NodeList from querySelectorAll() is static.
Here’s a complete demo you can use to see the difference:
<div id="container">
<p>Paragraph 1</p>
</div>
<script>
// HTMLCollection – LIVE
const htmlColl = document.getElementsByTagName("p");
console.log("Initial HTMLCollection length:",htmlColl.length); // 1
// Add a new paragraph
const newP = document.createElement("p");
newP.textContent = "Paragraph 2";
document.getElementById("container").appendChild(newP);
console.log("After DOM change – HTMLCollection length:",htmlColl.length); // 2
</script>
<div id="container">
<p>Paragraph 1</p>
</div>
<script>
// NodeList from querySelectorAll – STATIC
const nodeList = document.querySelectorAll("p");
console.log("Initial NodeList length:", nodeList.length); // 1
// Add a new paragraph
const newP = document.createElement("p");
newP.textContent = "Paragraph 2";
document.getElementById("container").appendChild(newP);
console.log("After DOM change – NodeList length:",nodeList.length); // Still 1; no update
</script>
Content:
HTMLCollection = only elements nodes;
NodeList = all nodes
Best practice: When you need array methods or don’t want surprises, convert both with
Array.from()
Conclusion
HTMLCollection and NodeList are both useful, but they are not interchangeable.
Use querySelectorAll() + NodeList when you want modern iteration as well as a stable snapshot.
Use getElementsByTagName/getElementsByClassName + HTMLCollection when you need live updates.
And remember the golden rule: when in doubt, turn it into a real array with Array.from().
Master these two and your DOM code becomes cleaner, faster, and far less error-prone. Happy coding!





Top comments (0)