Difference between HTMLCollection, NodeLists, and arrays of objects
Asked Answered
G

6

119

I've always been confused between HTMLCollections, objects, and arrays when it comes to DOM. For instance...

  1. What is the difference between document.getElementsByTagName("td") and $("td")?
  2. $("#myTable") and $("td") are objects (jQuery objects). Why is console.log also showing the array of DOM elements beside them, and are they not objects and not an array?
  3. What is the elusive "NodeLists" all about, and how do I select one?

Please also provide any interpretation of the below script.

[123,"abc",321,"cba"]=[123,"abc",321,"cba"]
{123:123,abc:"abc",321:321,cba:"cba"}=Object { 123=123, abc="abc", 321=321, more...}
Node= Node { ELEMENT_NODE=1, ATTRIBUTE_NODE=2, TEXT_NODE=3, more...}
document.links= HTMLCollection[a #, a #]
document.getElementById("myTable")= <table id="myTable">
document.getElementsByClassName("myRow")= HTMLCollection[tr.myRow, tr.myRow]
document.getElementsByTagName("td")= HTMLCollection[td, td, td, td]
$("#myTable")= Object[table#myTable]
$("td")= Object[td, td, td, td]


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> 
        <title>Collections?</title>  
        <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript"></script> 
        <script type="text/javascript">
            $(function(){
                console.log('[123,"abc",321,"cba"]=',[123,"abc",321,"cba"]);
                console.log('{123:123,abc:"abc",321:321,cba:"cba"}=',{123:123,abc:"abc",321:321,cba:"cba"});
                console.log('Node=',Node);
                console.log('document.links=',document.links);
                console.log('document.getElementById("myTable")=',document.getElementById("myTable"));
                console.log('document.getElementsByClassName("myRow")=',document.getElementsByClassName("myRow"))
                console.log('document.getElementsByTagName("td")=',document.getElementsByTagName("td"));
                console.log('$("#myTable")=',$("#myTable"));
                console.log('$("td")=',$("td"));
            });
        </script>
    </head>

    <body>
        <a href="#">Link1</a>
        <a href="#">Link2</a>
        <table id="myTable">
            <tr class="myRow"><td>td11</td><td>td12</td></tr>
            <tr class="myRow"><td>td21</td><td>td22</td></tr>
        </table>
    </body> 
</html>
Graecize answered 2/4, 2013 at 11:50 Comment(1)
I think I might add the following for posterity. (a) In modern JavaScript, a better comparison would be between document.querySelectorAll('td') and $('td'). (b) The fundamental difference is that jQuery works with its own type of object which contains, among other things, a numbered collection of HTML elements; this collection is none of the above, and the jQuery object is essentially a wrapper around the true DOM elements.Sharpfreeze
D
133

First I will explain the difference between NodeList and HTMLCollection.

Both interfaces are collections of DOM nodes. They differ in the methods they provide and in the type of nodes they can contain. While a NodeList can contain any node type, an HTMLCollection is supposed to only contain Element nodes.
An HTMLCollection provides the same methods as a NodeList and additionally a method called namedItem.

Collections are always used when access has to be provided to multiple nodes, e.g. most selector methods (such as getElementsByTagName) return multiple nodes or getting a reference to all children (element.childNodes).

For more information, have a look at DOM4 specification - Collections.

What is the difference between document.getElementsByTagName("td") and $("td")?

getElementsByTagName is method of the DOM interface. It accepts a tag name as input and returns a HTMLCollection (see DOM4 specification).

$("td") is presumably jQuery. It accepts any valid CSS/jQuery selector and returns a jQuery object.

The biggest differences between standard DOM collections and jQuery selections is that DOM collections are typically live (not all methods return a live collection though), i.e. any changes to the DOM are reflected in the collections if they are affected. They are like a view on the DOM tree, whereas jQuery selections are snapshots of the DOM tree in the moment the function was called.

Why is console.log also showing the array of DOM elements beside them, and are they not objects and not an array?

jQuery objects are array-like objects, i.e. they have numeric properties and a length property (keep in mind that arrays are just objects themselves). Browsers tend to display arrays and array-like objects in a special way, like [ ... , ... , ... ].

What is the elusive "NodeLists" all about, and how do I select one?

See the first part of my answer. You cannot select NodeLists, they are the result of a selection.

As far as I know there is not even a way to create NodeLists programatically (i.e. creating an empty one and adding nodes later on), they are only returned by some DOM methods/properties.

Daunt answered 2/4, 2013 at 12:6 Comment(11)
Thanks Felix. I've always been hesitant to read the DOM specifications, but I suppose I should do so. Are not the jQuery objects live since if changed, the DOM changes?Graecize
@user1032531: Well, if you make any change to one of the selected DOM elements (e.g. adding a child), then you will of course see the change since it's one and the same DOM element. But, assuming you selected all td elements, adding a new td element later on will not update the selection automatically to contain the new element.Daunt
I feel I know more, but am not perfectly clear. In addition to the DOM spec, do you know of any good articles which discuss this topic?Graecize
@FelixKling: You should mention that not all NodeLists are live.Speedway
@Bergi: Ah right... querySelector does not return a live list right? The specification said all collections are live unless otherwise noted, and there was nothing special mentioned for NodeList. But I guess it meant that it has to be noted for the method that returns the NodeList. Will update my answer.Daunt
"(some browsers chose to return HTMLCollection instead, which is OK, since it is a superset of NodeList)." I think you mean NodeList is a superset of HTMLCollection ?Zuniga
@caesarwang: Nope. I'm referring to the API. "An HTMLCollection provides the same methods as a NodeList and additionally a method called namedItem."Daunt
I wish they were all arraysManful
It seems also that methods "keys", "entries" and "forEach" i present in NodeList, but missing in HTMLCollectionCodycoe
@KrzysztofGrzybek That's right and it's super annoying. Why the heck one of them has .forEach() and the other doesn't?Martella
nodeList has forEach() method but HTMLcollection doesn't.Ernest
J
37

0. What is the difference between an HTMLCollection and a NodeList?

Here are some definitions for you.

DOM Level 1 Spec - Miscellaneous Object Definitions:

Interface HTMLCollection

An HTMLCollection is a list of nodes. An individual node may be accessed by either ordinal index or the node's name or id attributes. Note: Collections in the HTML DOM are assumed to be live meaning that they are automatically updated when the underlying document is changed.

DOM Level 3 Spec - NodeList

Interface NodeList

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.

The items in the NodeList are accessible via an integral index, starting from 0.

So they can both contain live data which means that the DOM will update when their values do. They also contain a different set of functions.

You will note if you inspect the console if you run your scripts that the table DOM element contains both a childNodes NodeList[2] and a children HTMLCollection[1]. Why are they different? Because HTMLCollection can only contain element nodes, NodeList also contains a text node.

enter image description here

1. What is the difference between document.getElementsByTagName("td") and $("td")?

document.getElementsByTagName("td") returns an array of DOM elements (a NodeList), $("td") is called a jQuery object which has the the elements from document.getElementsByTagName("td") on its properties 0, 1, 2, etc. The main difference is that the jQuery object is a little slower to retrieve but gives access to all the handy jQuery functions.

2. $("#myTable") and $("td") are objects (jQuery objects). Why is console.log also showing the array of DOM elements beside them, and are they not objects and not an array?

They are objects with their properties 0, 1, 2, etc. set to the DOM elements. Here's a simple example: of how it works:

jsFiddle

    var a = {
        1: "first",
        2: "second"
    }
    alert(a[1]);

3. What is the elusive "NodeLists" all about, and how do I select one?

You have been retrieving them in your code, getElementsByClassName and getElementsByTagName both return NodeLists

NodeList

Jardiniere answered 2/4, 2013 at 12:7 Comment(5)
How did you display the DOM in your 3rd response? Thanks!Graecize
@Graecize that is the Chrome dev tools. By the way I updated the start of the answer.Jardiniere
The array-like log is mostly a result of the length property, not of the numeric property names. And what does your example of alerting a string have to do with console.log?Speedway
That was showing how you can have numeric properties on objects. I'm trying to emphasis the facts that they're objects, not arrays.Jardiniere
Number 3 is completely wrong. getElementsByClassName and getElementsByTagName return HTMLCollections and NOT NodeLists. querySelector and querySelectorAll will return a NodeList.Gnni
M
18

Additional note

What is the difference between a HTMLCollection and a NodeList?

A HTMLCollection contains only element nodes (tags) and a NodeList contains all nodes.

Most important node types:

  1. element node
  2. attribute node
  3. text node
  4. comment node

node types

Whitespace inside elements is considered as text, and text is considered as nodes.

Consider the following:

<ul id="myList">
  <!-- List items -->
  <li>List item 1</li> 
  <li>List item 2</li>
  <li>List item 3</li>
  <li>List item 4</li>
  <li>List item 5</li>
</ul>

Whitespace: <ul id="myList"> <li>List item</li></ul>

No whitespace: <ul id="myList"><li>List item</li></ul>

Difference between HTMLCollection and a NodeList

Musset answered 1/6, 2018 at 10:55 Comment(0)
O
10

So much has been told already, but thought a more summarised version of answer with an example to explain the differences between HTMLCollection and NodeList would help.

Types of Nodes in DOM

  • There are 12 different node types, which may have children of various node types:

enter image description here

  • We can use the following three properties to inspect and inquire about nodes in DOM:

    • nodeType Property
    • nodeName Property
    • nodeValue Property
  • The nodeType property returns the node type, as a number, of the specified node.

    • If the node is an element node, the nodeType property will return 1.
    • If the node is an attribute node, the nodeType property will return 2.
    • If the node is a text node, the nodeType property will return 3.
    • If the node is a comment node, the nodeType property will return 8.
    • This property is read-only.

HTMLCollection vs NodeList

enter image description here

We can understand the differences between HTMLCollection and NodeList more clearly with the following example. Please, try to check the outputs in your own browser console to have a better understanding.

<ul>
  <li>foo</li>
  <li>bar</li>
  <li>bar</li>
</ul>
// retrieve element using querySelectorAll
const listItems_querySelector = document.querySelectorAll('li');
console.log('querySelector', listItems_querySelector);

// retrieve element using childNodes
const list  = document.querySelector('ul')
const listItems_childNodes = list.childNodes;
console.log('childNodes', listItems_childNodes);
const listItems_children = list.children;
console.log('children', listItems_children);

const listItems_getElementsByTagName = document.getElementsByTagName('li');
console.log('getElementsByTagName', listItems_getElementsByTagName);

console.log('*************************');
console.log('add one list item');
console.log('*************************');
list.appendChild(document.createElement('li'));

console.log('querySelector', listItems_querySelector);
console.log('childNodes', listItems_childNodes);
console.log('children', listItems_children);
console.log('getElementsByTagName', listItems_getElementsByTagName);

console.log('*************************');
console.log('add one more list item');
console.log('*************************');
listItems_getElementsByTagName[0].parentNode.appendChild(document.createElement('li'));

console.log('querySelector', listItems_querySelector);
console.log('childNodes', listItems_childNodes);
console.log('children', listItems_children);
console.log('getElementsByTagName', listItems_getElementsByTagName); 
Olivero answered 1/6, 2020 at 20:31 Comment(0)
L
2

$("td") is extended jQuery object and it has jQuery methods, it returns jquery object that contains array of html objects. document.getElementsByTagName("td") is raw js method and returns NodeList. See this article

Lamentation answered 2/4, 2013 at 12:2 Comment(2)
Thanks Karaxuna. Yes, I had looked at that article. Don't know if it helped, but definitely made me ask more questions :)Graecize
Thanks @karaxuna. Useful article, very well explained.Representationalism
A
1

The NodeList objects are collections of Node's, returned for example by x.childNodes property or document.querySelectorAll() method. In some cases, the NodeList is live, which means that changes in the DOM automatically update the collection! For example, Node.childNodes is live:

var c = parent.childNodes; //assume c.length is 2
parent.appendChild(document.createElement('div'));
//now c.length is 3, despite the `c` variable is assigned before appendChild()!!
//so, do not cache the list's length in a loop.

But in some other cases, the NodeList is static, where any changes in the DOM does not affect the content of the collection. querySelectorAll() returns a static NodeList.

The HTMLCollection is a live and ordered collection of elements (it is automatically updated when the underlying document is changed). It can be result of properties like as children or methods like as document.getElementsByTagName(), and could only have HTMLElement's as their items.

HTMLCollection also exposes its members directly as properties by both name and index:

var f = document.forms; // this is an HTMLCollection
f[0] === f.item(0) === f.myForm //assume first form id is 'myForm'

The HTMLElement is just one type of Nodes:

Node << HTMLElement inheritance

The Node can be several types. Most important ones are as following:

  • element (1): An Element node such as <p> or <div>.
  • attribute (2): An Attribute of an Element. The element attributes are no longer implementing the Node interface in DOM4 specification!
  • text (3): The actual Text of Element or Attribute.
  • comment (8): A comment node.
  • document (9): A document node.

So, a big difference is that HTMLCollection contains only HTMLElements but NodeList also contains the comments, white-space texts (carriage return chars, spaces..), etc. Check it as in following snippet:

function printList(x, title) {
  console.log("\r\nprinting "+title+" (length="+x.length+"):");
  for(var i=0; i<x.length; i++) {
    console.log("  "+i+":"+x[i]);
  }
}

var elems = document.body.children; //HTMLCollection
var nodes = document.body.childNodes; //NodeList

printList(elems, "children [HTMLCollection]");
printList(nodes, "childNodes [NodeList]");
<div>para 1</div><!-- MyComment -->
<div>para 2</div>

Both HTMLCollection and NodeList contain the length property you can use to loop over their items. Don't use for...in or for each...in to enumerate the items in NodeLists, since they will also enumerate its length and item properties and cause errors if your script assumes it only has to deal with element objects. Also, for..in is not guaranteed to visit the properties in any particular order.

for (var i = 0; i < myNodeList.length; i++) {
  var item = myNodeList[i];
}
Aimee answered 2/10, 2018 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.