- Home
- /
- Tutorials
- /
- JavaScript Tutorial
- /
- JavaScript DOM Collections
JavaScript DOM
JavaScript DOM Collections
querySelectorAllreturns a NodeList, which looks like an array but is not one.
A NodeList has a length and can be indexed, so it feels like an array.
It does not have map or filter, which is where confusion starts.
Example
Example
javascript
document.body.innerHTML = "<p>A</p><p>B</p>";
const items = document.querySelectorAll("p");
console.log(items.length);
console.log(Array.isArray(items));The output is 2 then false.
NodeList and HTMLCollection
querySelectorAll returns a static NodeList, which has forEach.
children and getElementsByTagName return a live HTMLCollection, which does not.
Static means it does not update when the page changes; live means it does.
Syntax
Syntax
javascript
const list = document.querySelectorAll("p");
const array = Array.from(list);Array.from turns either kind into a real array.
Static Versus Live
This difference causes real bugs, so it is worth seeing directly.
Example
Example
javascript
document.body.innerHTML = '<div id="box"><p>A</p></div>';
const box = document.getElementById("box");
const staticList = document.querySelectorAll("#box p");
const liveList = box.getElementsByTagName("p");
box.append(document.createElement("p"));
console.log(staticList.length);
console.log(liveList.length);The static list still says 1; the live one says 2.
forEach Works on a NodeList
This is the one array method a NodeList does have.
Example
Example
javascript
document.body.innerHTML = "<p>A</p><p>B</p>";
let text = "";
document.querySelectorAll("p").forEach(function (p) {
text += p.textContent;
});
console.log(text);The output is AB.
Getting a Real Array
Array.from or spread unlocks every array method.
Example
Example
javascript
document.body.innerHTML = "<p>A</p><p>B</p><p>C</p>";
const texts = Array.from(document.querySelectorAll("p")).map(function (p) {
return p.textContent;
});
console.log(texts.join("-"));The output is A-B-C.
Filtering Elements
Once it is an array, filter works as normal.
Example
Example
javascript
document.body.innerHTML =
'<p class="on">A</p><p>B</p><p class="on">C</p>';
const on = [...document.querySelectorAll("p")].filter(function (p) {
return p.classList.contains("on");
});
console.log(on.length);A CSS selector could have done this too, but the pattern is general.
A Live Collection in a Loop
Removing elements while looping over a live collection skips items.
The list shrinks underneath the loop as it runs.
Example
Example
javascript
document.body.innerHTML = '<div id="box"><p>A</p><p>B</p></div>';
const box = document.getElementById("box");
Array.from(box.getElementsByTagName("p")).forEach(function (p) {
p.remove();
});
console.log(box.children.length);Copying to an array first makes the loop safe.
Complete Example
Complete Example
html
<!DOCTYPE html>
<html>
<head>
<title>JavaScript DOM Collections</title>
</head>
<body>
<h1>DOM Collections</h1>
<p>One</p>
<p>Two</p>
<p>Three</p>
<p id="out"></p>
<script>
const items = document.querySelectorAll("p");
const texts = Array.from(items).map(function (p) {
return p.textContent;
});
document.getElementById("out").textContent = texts.length + " paragraphs";
</script>
</body>
</html>Try It Yourself
Run the above example in the Try It Editor.
Try filtering:
Keep only the texts longer than three characters before counting.
Important Points
querySelectorAllreturns a static NodeList.childrenreturns a live HTMLCollection.- A NodeList has
forEachbut notmaporfilter. Array.fromor spread gives a real array.- Copy a live collection before removing elements from it.
Conclusion
DOM collections are array-like but not arrays.
Converting to an array once removes the whole class of problem.
The live versus static difference is a favourite interview question.
