Understanding the DOM in JavaScript: HTMLCollection vs NodeList Explained

In the early days of web, browsers like Internet Explorer and Netscape Navigator had their own ways of exposing HTML elements to JavaScript.
But there was no easy way for developers to access things like forms, images, links or html tags. And even if they could, each browser had different methods and APIs for it, this led to code written for one browser not working for another.
So, DOM or the Document Object Model was introduced to provide a standardized way to represent and Interact with HTML document using JavaScript.
What is DOM?
DOM or the Document Object Model, is a tree structure introduced for the purpose of representing a HTML document.
Let's write a simple HTML code to understand it better
<!DOCTYPE html>
<head>
<title>Dynamic List Creator</title>
</head>
<body>
<div id="container">
<h1 id="heading"> Hello World! </h1>
<p id= "para"> I love JavaScript </p>
</div>
</body>
</html>
We will try to build a DOM for this code.
First of all, all browsers have a window object, this object represents the entire tab in which a web is loaded. So, this will be the first part of our DOM.
Window: It is the top level container for everything running in a browser.
A browser already contains many built-in variables and functions, such as console object, alert(), setTimeout(), and others. All of these are also stored in the windows object.
For example,
window.alert("Hello");
window.console.log("Hello");
However, since window is the global object of a browser, it can be omitted. So we can also write the above code as
alert("Hello");
console.log("Hello");
The browser automatically assumes window when accessing these global properties.
Now the next part of the DOM, is our HTML document itself.
document: The document part of the DOM contains the HTML we have loaded in the browser.
It has all the elements written inside the HTML, such as <html>, <head>, <body> and all the tags inside them.
In other words, the document object is the entry point to the DOM, and JavaScript uses it to access and manipulate elements on the page.
It contains several methods to access different components of the HTML code
For example
document.getElementById("container")
Here, JavaScript asks the document to find an element with id "container"
There are several more methods to access the components using different means, such as
document.getElementById()
document.getElementsByClassName()
document.getElementsByTagName()
document.querySelector()
document.querySelectorAll()
Now, after this level of understanding of the DOM. A clear DOM structure would be easier to understand.
Lets check our HTML file again
<!DOCTYPE html>
<head>
<title>Document Object Model</title>
</head>
<body>
<div id="container">
<h1 id="heading"> Hello World! </h1>
<p id= "para"> I love JavaScript </p>
</div>
</body>
</html>
Let's draw the DOM tree structure, as we go through this file
We start of with the window and document object
Now, in the code we have a html tag <!DOCTYPE html> , let's add it
Now, inside <html> tag, we have two tags
<head>tag<body>tag
We get
Now, let's focus inside the <head> tag
We have a <title> tag with text inside it, so let's draw for <head>
This completes our <head> tag.
Now, it's time for the <body> tag. Inside <body> we have a <div> tag
Inside <div> tag, we have two more tags
heading tag
<h1>paragraph tag
<p>
Let's complete the DOM for <body>
This completes the DOM structure for our HTML file.
Finally, the whole DOM structure looks like
Now that we have understood how a DOM tree is built from scratch, I hope you have a great understanding of DOM now.
What are Nodes?
In the DOM tree, every part of the document is represented as a node.
Different parts of the tree such as the document itself, HTML elements, text inside elements, attributes, and even comments are all treated as nodes.
There are several types of nodes, the most prominent ones being
Document Node - This is the top-most node, representing the whole HTML file.
Element Nodes - These are the nodes representing the HTML tags
Text Nodes - These nodes represent the text values contained inside an HTML tag.
Attribute Nodes - The attributes of an html tag are represented by these nodes.
Comment Nodes - The comments in a file also becomes part of the DOM.
When the DOM specification was standardized by World Wide Web Consortium, the goal was to represent all parts of the document as nodes.
Therefore NodeList was introduced.
The idea was:
“A collection that can contain any DOM node.”
So NodeList became the generic container for nodes.
NodeList
NodeList are array-like collection that can store the DOM nodes.
These nodes can include all types of nodes such as element, text, or comment nodes.
Unlike regular JavaScript arrays, a NodeList does not support all array methods.
Now, the first question that comes to mind
How to use these NodeList collections?
To answer that , let's go back to our discussion about document. Remember I mentioned certain methods that document contain to access different elements of DOM?
Let's list one such method
document.querySelectorAll()
This document method actually returns a NodeList.
Let's take an example of HTML code to understand it even better
<!DOCTYPE html>
<head>
<title>Understanding NodeList</title>
</head>
<body>
<div class="container">
<h1 class="nodeList">Hello World!</h1>
<p class = "nodeList">I love JavaScript</p>
<p class = "nodeList">I love HTML</p>
<span class = "notNodeList"> I love CSS</span>
</div>
</body>
</html>
Now, we run document.querySelectorAll(".nodeList") on the browser console
we get a NodeList of HTML elements that shared the class "nodeList".
From the above, we can observe a couple of things
NodeList has indexes, same as array
It has a length property as well
Accessing Elements from a NodeList
Since a NodeList is an array-like structure, we can access elements using indexes.
For example:
This returns the first and second elements from the NodeList.
Iterating Through a NodeList
A NodeList also supports the forEach() method, which allows us to loop through all the nodes.
This prints each element inside the NodeList.
The childNodes Property
Apart from querySelectorAll(), childNodes is another common way to get a NodeList.
From our previous HTML example
<!DOCTYPE html>
<head>
<title>Understanding NodeList</title>
</head>
<body>
<div class="container">
<h1 class="nodeList">Hello World!</h1>
<p class = "nodeList">I love JavaScript</p>
<p class = "nodeList">I love HTML</p>
<span class = "notNodeList"> I love CSS</span>
</div>
</body>
</html>
we can access the children of the container class node
An interesting thing to note is, even the linebreaks in the HTML code are treated as text nodes in this case.
childNodes includes text nodes as well, such as spaces and line breaks.
Static vs Live NodeList
NodeLists can be static or live, depending on how they are created.
A static NodeList does not change when the DOM changes.
It is created using
document.querySelectorAll()
If new elements are added later, this NodeList will not update automatically.
On the other hand, a live NodeList updates automatically when the DOM changes
element.childNodes
returns a live NodeList.
NodeList is one type of collection returned by DOM methods. However, it is not the only type of collection used in the DOM.
In fact, before NodeList became widely used in modern DOM APIs, browsers already had another type of collection called HTMLCollection, which was designed specifically to store HTML elements.
HTMLCollection
An HTMLCollection is also an array-like collection.
Unlike NodeList, which can contain different types of nodes, an HTMLCollection contains only element nodes (HTML tags such as <div>, <p>, <h1>, etc.).
There are several methods that return HTMLCollection, naming a few:
document.getElementsByClassName()
document.getElementsByTagName()
element.children
Let's bring back our previous HTML code to understand these better
<!DOCTYPE html>
<head>
<title>Understanding HTMLCollection</title>
</head>
<body>
<div class="container">
<h1 class="htmlCollection">Hello World!</h1>
<p class = "htmlCollection">I love JavaScript</p>
<p class = "htmlCollection">I love HTML</p>
<span class = "nothtmlCollection"> I love CSS</span>
</div>
</body>
</html>
Now if we use document.getElementsByClassName() to access some of the elements, we get
Notice that, similar to NodeList, HTMLCollection also has indexes and a length property.
Accessing Elements from an HTMLCollection
We can access the elements of a HTMLCollection using index
we can also use the length property
Iterating Through a HTMLCollection
unlike NodeList, an HTMLCollection does not support the forEach() method directly. Therefore, we usually iterate through it using a for loop.
This loop goes through each element in the HTMLCollection and prints it in the console.
Live Nature of HTMLCollection
One of the key characteristics of HTMLCollection is that it is a live collection.
This means if the DOM changes (for example, if a new element with the same class is added), the HTMLCollection will automatically update.
This might sound like a great feature at first, but it has it's issues
Let's write a code to remove all the element nodes with class = "htmlCollection"
now when we will try to access elements variable, what do you expect?
Let's check
wait a min! how's the <p> tag still there? didn't we loop through all the elements and removed them?
The catch:
Because HTMLCollection is live, the collection updates every time an element is removed. As a result, the indexes shift, and some elements may get skipped during the loop.
For example, if the collection initially looks like this:
[Element1, Element2, Element3]
After removing the first element, the collection becomes:
[Element2, Element3]
But the loop counter moves to the next index, causing Element2 to be skipped.
And hence, Element2 or the <p> tag survived the removal.
In modern JavaScript development, developers often prefer querySelectorAll() because it returns a static NodeList, which avoids unexpected behavior caused by live collections.
Key Differences Between NodeList and HTMLCollection
| Feature | NodeList | HTMLCollection |
|---|---|---|
| What it stores | Can store any type of DOM node (elements, text nodes, comments) | Stores only HTML element nodes |
| Returned by | querySelectorAll(), childNodes |
getElementsByClassName(), getElementsByTagName(), children |
| Live or Static | Can be live or static depending on the method | Always live |
| Updates when DOM changes | Static NodeList does not update automatically | Automatically updates when DOM changes |
Supports forEach() |
Yes | No |
| Iteration | Can use forEach() or loops |
Usually iterated using for loop |
| Node types included | Elements, text nodes, comment nodes | Only HTML elements |
Conclusion
Understanding NodeList and HTMLCollection is an important step in becoming comfortable with working with the DOM in JavaScript.
In this article we learned:
What the DOM (Document Object Model) is and how browsers represent HTML as a tree structure
The role of the window and document objects in accessing and interacting with the DO
What nodes are and the different types of nodes present in the DOM
What a NodeList is and how it can store different types of DOM nodes
The difference between static and live NodeLists
What an HTMLCollection is and how it stores only HTML elements
The live nature of HTMLCollection and how it can sometimes lead to unexpected behavior
The key differences between NodeList and HTMLCollection
With this understanding, you can now confidently work with DOM collections and choose the right method depending on your use case.




