From MDN for NodeList:
In some cases, the NodeList is a live collection, which means that changes in the DOM are reflected in the collection. For example, Node.childNodes is live:
var parent = document.getElementById('parent'); var child_nodes = parent.childNodes; console.log(child_nodes.length); // let's assume "2" parent.appendChild(document.createElement('div')); console.log(child_nodes.length); // should output "3"
In other cases, the NodeList is a static collection, meaning any subsequent change in the DOM does not affect the content of the collection. document.querySelectorAll returns a static NodeList.
So .... kind of annoying! Is there any central reference for which methods return live lists and which ones return static lists, without having to check individually for all the various parts of the DOM API? Is there any rule at work here?
Information about each method details if it is live or not, but there doesn't seem to be a standard convention for determining it.
document.getElementsByClassName()
is an HTMLCollection
and is live.
document.getElementsByTagName()
is an HTMLCollection
and is live.
document.getElementsByName()
is a NodeList
and is live.
document.querySelectorAll()
is a NodeList
and is not live.
HTMLCollection
s are always live.
An
HTMLCollection
is a list of nodes. An individual node may be accessed by either ordinal index or the node'sname
orid
attributes.Note: Collections in the HTML DOM are assumed to be live meaning that they are automatically updated when the underlying document is changed.
http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-75708506
A NodeList object is a collection of nodes... The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
http://www.w3.org/TR/DOM-Level-3-Core/core.html#td-live
So, both HTMLCollection
s and NodeList
s are collections.
An HTMLCollection
is always a live representation of its Element
s' presence in the DOM, whereas a NodeList
is a more generic construct with Node
s that may or may not be in the DOM.
A collection is an object that represents a list of DOM nodes. A collection can be either live or static. Unless otherwise stated, a collection must be live.
http://www.w3.org/TR/2012/WD-dom-20120405/#collections
So, to summarize:
.querySelectorAll()
returns a NodeList that is static, meaning that.querySelectorAll()
returns a collection that is not in the DOMNote that "not being in the DOM" doesn't mean the elements within static collections are removed, detached, hidden, or inaccessible. All it means is that the collection is fixed to whatever the selector matches at the time you initiate it.
Well, here is a method to determine if a collection is live. It appends a clone of a member of the collection (so it will match the selector) to its parent, checks to see if the length changed, and then removes it so the page is not affected.
function isLive(collection) {
if (HTMLCollection.prototype.isPrototypeOf(collection)) return true // HTMLCollections are always live
const length = collection.length;
if (!length) return undefined; // Inconclusive
const el = collection.item(0);
const parent = el.parentNode;
const clone = el.cloneNode();
clone.style.setProperty('display', 'none', 'important');
parent.appendChild(clone);
const live = collection.length !== length;
parent.removeChild(clone);
return live;
}
const divs1 = document.getElementsByClassName('c');
const divs2 = document.getElementsByTagName('span');
const divs3 = document.getElementsByName('notFound');
const divs4 = document.querySelectorAll('.c');
console.log("document.getElementsByClassName('c'):", divs1.toString()); // [object HTMLCollection]
console.log("document.getElementsByTagName('notFound'):", divs2.toString()); // [object HTMLCollection]
console.log("document.getElementsByName('notFound'):", divs3.toString()); // [object NodeList]
console.log("document.querySelectorAll('.c'):", divs4.toString()); // [object NodeList]
console.log('isLive(divs1)', isLive(divs1)); // true
console.log('isLive(divs2)', isLive(divs2)); // true
console.log('isLive(divs3)', isLive(divs3)); // undefined
console.log('isLive(divs4)', isLive(divs4)); // false
<div>
<div class="c">C1</div>
<div class="c">C2</div>
</div>
<div>
<div class="c">C3</div>
<div class="c">C4</div>
</div>