NodeList object in javascript
Asked Answered
S

8

43

Can anyone tell me what kind of object the NodeList is. I read that it is an array-like object and that it can be accessed via bracket notation, for example var a = someNode.childNode[0];. How is this possible since via bracket notation we can only access to the properties of an object, and as i know we can not have

Surfbird answered 31/3, 2011 at 14:27 Comment(0)
D
51

A NodeList is collection of`DOM elements. It's like an array (but it isn't). To work with it, you must turn it into a regular JavaScript array. The following snippet can get the job done for you.

const nodeList = document.getElementsByClassName('.yourClass'),
      nodeArray = [].slice.call(nodeList);

UPDATE:

// newer syntax
const nodeList = Array.from(document.querySelectorAll('[selector]'))

// or
const nodeList = [...document.querySelectorAll('[selector]')]
Diactinic answered 8/8, 2013 at 15:14 Comment(8)
[].slice.call(nodeList) works like a charm for me. I would love to know why/how it works.Sunny
basically javascript treats the nodelist as an array because you are setting the contenxt of the empty array to be the nodeList using the call method. then, simply the slice method returns an exact copy of itself (when used with no arguments). i hope this answered your question (:Diactinic
So much more elegant than a for loop. Works great with Array.prototype.forEach too!Photoplay
@Diactinic that isn't exactly correct, and the similar [].forEach.call paradigm critique here - toddmotto.com/ditch-the-array-foreach-call-nodelist-hack - offers better context. To show how it doesn't exactly work as explained, trying running [1, 2, 3].slice.call(nodeList) and notice you get the same result. Try playing around with the number of entries in the original array, you still get the same result.Cabasset
getElementsByClassName returns an HTMLCollection, not a NodeList. This difference is important. https://mcmap.net/q/129581/-difference-between-htmlcollection-nodelists-and-arrays-of-objectsSoult
You are right, HTMLCollection can only contain elements, while NodeList can contain whatever type of node, like text nodes and comment nodes if I'm not mistaken. The example is clear though, it shows how to convert whatever DOM query response to a primitive array.Diactinic
@Cabasset I've read that article and it has some valid points. I guess it depends on the needs. Myself for example, I don't have the need to support IE since forever and I tend to favour functional-like programming, and a for loop is a little bit more "impure" than forEach recursion, in my opinion. Array.from(nodeList).map(n => n.textContent) is a one liner beauty, for example.Diactinic
Yes, if you really need to to convert a NodeList into an array, then Array.from(nodeList) or [...nodeList] are both great tools. However, if you find yourself writing [...nodeList].forEach(), then you can probably just iterate over the plain NodeList rather than spreading it out into an array. If you are worried about browser support, there is an extremely simple polyfill - if (window.NodeList && !NodeList.prototype.forEach) NodeList.prototype.forEach = Array.prototype.forEachCabasset
T
39

NodeList is a host object and is not subject to the usual rules that apply to native JavaScript objects. As such, you should stick to the documented API for it, which consists of a length property and access to its members via square bracket property access syntax. You can use this API to create an Array containing a snapshot of the NodeList's members:

var nodeList = document.getElementsByTagName("div");
var nodeArray = [];
for (var i = 0; i < nodeList.length; ++i) {
    nodeArray[i] = nodeList[i];
}
Tummy answered 31/3, 2011 at 14:59 Comment(4)
So does this mean that a nodeList cannot be traversed by a forin loop?Cherlycherlyn
@Triztian: Indeed it does. At least, there's no guarantee it will work, and I think it doesn't in IE.Tummy
It won't doesn't work in Google Chrome either, you have to use the length property and access the elements as if were an array.Cherlycherlyn
Aha, this clarifies why forEach works on type Array, but not with bilbo.getElementsByTagName("taggins").Broom
H
10

The NodeList is not a core Javascript object, it is provided by the Browser with the DOM. Think of a function which returns an interface to a dynamic or live object, so forEach() is not available, but you can convert it into a real array to have a snapshot with e.g.

// turns nodelist into an array to enable forEach
function toArray(list) {
  var i, array = [];
  for  (i=0; i<list.length;i++) {array[i] = list[i];}
  return array;
}

Details: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-536297177

Hexavalent answered 31/3, 2011 at 14:52 Comment(4)
Well i suppose that it is implemented in a language such as C++. Even so, how do they communicate with JavaScript code?Surfbird
@user512783: That depends on the browser and the javascript engine. Why do you want to know this?Bourges
i wanted to figure out if the DOM API is written in EcmaScript, but as i understand, it is not.Surfbird
So, your question is about the implementors sources? Go here for firefox: mxr.mozilla.org/mozilla-central/ident?i=nsIDOMNodeListHexavalent
U
9

NodeLists "live" which is to say that they are updated when the document structure changes such that they are always current with the most accurate information. In reality, all NodeList objects are queries that are run against the DOM whenever they are accessed.

Any time you want to iterate over a NodeList, it’s best to initialize a second variable with the length and then compare the iterator to that variable:

var divs = document.getElementsByTagName("div");

for (var i=0, lens=divs.length; i  <  len; i++){
    var div = document.createElement("div");
    document.body.appendChild(div);
} 

NodeList is an array like structure but it's not actually an array. You can access array values through bracket notation.

Upper answered 31/3, 2011 at 14:34 Comment(4)
What you just answered is written in the book i am reading. I can't what does array-like mean. The EcmaScript does not specify anything like this. I guess it is an implementation technique.Surfbird
It's not in the ECMA its DOM spec. You can iterate over it like an array (bracket notation) but you can’t use Array methods like push(), splice() or reverse() to manipulate it.Upper
That is to say, it runs in browser-level code (for example C++)?Surfbird
Browsers implement it, so probably yes.Upper
B
9

JavaScript is like Alcohol, it can coerce:

var links = document.getElementsByTagName('a');
Array.prototype.slice.call(links).forEach(function(anchor, index, arr) {
  anchor.addEventListener('click', onClickFN, false);
});
Bathyscaphe answered 4/8, 2013 at 0:20 Comment(2)
...or just Array.prototype.forEach.call(links, function() {}) ;)Bummer
or just [].slice.callTamie
M
7

Node lists are often implemented as node iterators with a filter. This means that getting a property like length is O(n), and iterating over the list by re-checking the length will be O(n^2).

var paragraphs = document.getElementsByTagName('p');
for (var i = 0; i < paragraphs.length; i++) {
  doSomething(paragraphs[i]);
}

It is better to do this instead:

var paragraphs = document.getElementsByTagName('p');
for (var i = 0, paragraph; paragraph = paragraphs[i]; i++) {
   doSomething(paragraph);
} 

This works well for all collections and arrays as long as the array does not contain things that are treated as boolean false.

In cases where you are iterating over the childNodes you can also use the firstChild and nextSibling properties.

var parentNode = document.getElementById('foo');
for (var child = parentNode.firstChild; child; child = child.nextSibling) {
  doSomething(child);
}
Moreta answered 20/8, 2014 at 10:8 Comment(1)
Ehm, not just boolean false values will break the loop, all falsey (undefined, null, 0 etc.) values would...Bummer
C
7

Now in ES2015 you can make use of Array.from method which creates an Array instance from any array-like object, so this should work :

const divList = Array.from( document.getElementsByTagName("div") );

For more information : https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from

Centimeter answered 28/5, 2016 at 8:26 Comment(1)
Worked great for me. Chrome, October 2016.Comfort
C
2

Summary:

A NodeList object is a data structure which represents a collection of Nodes. Nodes in the context of the DOM can be the following things:

  1. The document itself
  2. DOM elements (i.e. HTML/SVG elements)
  3. Text
  4. comments

A NodeList is not an array, however a NodeList is an iterable data structure which means we can loop over the values (i.e. the node items) using a for..of loop. Furthermore are their some nice utility function on the prototype of the NodeList which makes working with them more convenient.

Example:

const parent = document.querySelector('.parent');
const myNodeList1 = parent.childNodes; // this property is a Nodelist
console.log(myNodeList1 instanceof NodeList);

const myNodeList2 = document.querySelectorAll('.foo'); // this method returns a Nodelist
console.log(myNodeList2 instanceof NodeList);

// looping over the items of a nodelist
for (let el of myNodeList2) {
  el.innerHTML = 'hi';
}

// getting the length of a nodeList 
console.log(myNodeList2.length);
<div class="parent">
  <div class="foo"></div>
  <div class="foo"></div>
</div>

Here is what a Nodelist looks like in the browser (chrome) devtools:

enter image description here


You can access the elements of a NodeList with the following notation:

 myNodelist[0]; // grabs the first item of the NodeList

Because you are simply a property value of the object using a key. In this example the key of the property was the number zero, and value was the DOM element.

Convoke answered 5/12, 2018 at 12:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.