HTML5 Development Center

Developed for you in part by
 
532-javascript-collections

JavaScript vs jQuery HTML Collections

By | | JavaScript & Ajax Tutorials | JavaScript & CSS

HTMLCollection objects are returned from document.getElementsByTagName, document.getElementsByName and document.getElementsByClassName methods (not supported in all browsers). Superficially, they’re similar to arrays since they have length property and elements can be accessed by [index]. However, they’re not arrays; methods such as push(), slice() and sort() are not supported.

Consider the following HTML document:


<body>
	<p>Paragraph 1</p>
	<p>Paragraph 2</p>
	<p>Paragraph 3</p>
</body>

Let’s grab every paragraph node using getElementsByTagName and a jQuery selector:


var pCollection = document.getElementsByTagName("p");
var pQuery = $("p");
console.log("pCollection.length: ", pCollection.length);
console.log("pQuery.length: ", pQuery.length);

Both return the same nodes so the collection length is 3:


pCollection.length: 3
pQuery.length: 3

We’ll now add another paragraph element to the document and look at the collections again:


// add new paragraph
var newp = document.createElement("p");
newp.appendChild(document.createTextNode("Paragraph 4"));
document.body.appendChild(newp);
//
// display length
console.log("pCollection.length: ", pCollection.length);
console.log("pQuery.length: ", pQuery.length);

The result:


pCollection.length: 4
pQuery.length: 3

HTMLCollection objects are live. They are automatically updated whenever the underlying document changes. jQuery and most other JavaScript libraries use methods such as document.getElementsByTagName() but copy the resulting nodes to a real array. Therefore, it’s a query on the state of the document at that time: it is never updated.

There are advantages and disadvantages with both methods. For example, the following code causes an infinite loop since the HTMLCollection length increases as <p> elements are added:


var pCollection = document.getElementsByTagName("p");
for (var i = 0; i < pCollection.length; i++) {
	document.body.appendChild(pCollection[i].cloneNode(true));
}

That said, there may be situations when a faster, native live HTMLCollection is more useful than a static collection of jQuery nodes or repeatedly making the same selection. Fortunately, we can pass any collection to jQuery when we want to manipulate it, e.g.


var pCollection = document.getElementsByTagName("p");
// ... add nodes, do work, etc ...
$(pCollection).addClass("myclass");

jQuery and other libraries can cut development effort but always check whether it’s possible to write more efficient code in plain old JavaScript without incurring additional file requests and processing overheads.

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

More Posts - Website

{ 17 comments }

Han Wei June 21, 2011 at 6:39 pm

Nice article!

Minor nitpick:

“…there may be situations when a faster, native live HTMLCollection is more useful than a static collection of jQuery nodes or repeatedly making the same selection.”

This is misleadingly implies that the native HTMLCollection is faster than jQuery collections, which is never the case, because every access to an item in the HTMLCollection is as slow as running a new jQuery call, whereas accessing the essentially cached jQuery items is almost negligible time.

While performance usually isn’t a concern at all in JavaScript, if it ever is a problem the culprit is almost *always* from touching the DOM, even something as simple as accessing an HTMLCollection. (For example, I recently encountered an issue where setting the value attribute of a textarea takes in excess of 15ms, which in a loop of 50-some times was causing the page to hang for almost a second, whereas the rest of the JavaScript being run combined took 1 or 2ms, max.)

molokoloco May 31, 2011 at 3:59 pm

Resolved, but only work for one tag at a time…
var s = function(t) { return document.getElementsByTagName(t); };
alert(s(‘p’).length);

;)

Smola May 31, 2011 at 1:29 pm

This was enlightening! Thanks Craig!

As an aside, I’d love to see a series of posts that pit regular JS vs. jQuery for common tasks to see which is actually more efficient. I feel shamed that I’ve most likely been subtracting from run-time efficiency to decrease development time. =[

molokoloco May 31, 2011 at 5:04 am

Yes, $allParagraphs = $(‘p’) would hold all with distinct nodes as reference. So there only when creating/inserting/ordering main nodes elements that the $allParagraphs var is not updated.
But if you change one node properties like $(‘p’).eq(2).text(‘New text’) you have no problem to fetch it with alert($allParagraphs[2].text()) …

Craig Buckler May 31, 2011 at 5:30 am

The article illustrates the difference between live HTMLCollections and jQuery’s static array of nodes. If the document or selection doesn’t change, jQuery’s nodes will remain valid.

However, if you removed a <p> node, $allParagraphs[2] may not exist. It’d would remain in jQuery’s array but most methods would ignore it.

Ilya Vassilevsky May 31, 2011 at 12:23 am

OMG this is fantastic!

molokoloco May 30, 2011 at 5:10 pm

It’s a better practice to assign jQuery elements to variable like this
$allParagraphs = $(‘p’);
But if you manipulate hardly the DOM, you should not work with it… Yep.
$(‘p’).length(); will always works…

Craig Buckler May 31, 2011 at 1:19 am

I’m not sure I understand your comment?

$allParagraphs = $(‘p’) would hold all <p> nodes at the moment it’s run. If you added or removed nodes from the document, $allParagraphs would never change. That might be useful or not depending on what you’re trying to achieve.

IT Mitică May 30, 2011 at 2:13 pm

A good point.

You’re saying jQuery passes the parameters by value, not by reference.

But…

While

- the assignment operation for pCollection means this will became more of a handler, and that each iteration of pCollection means a complete reevaluation for the document.getElementsByTagName() function, which also means slower execution,

- the assignment operation for pQuery means the code is actually executed on the spot, and each iteration pQuery means faster execution?

Craig Buckler May 31, 2011 at 1:16 am

Not quite.

document.getElementsByTagName(“p”) returns a live HTMLCollection. It’ll always hold every <p> node no matter how the document changes. The speed will remain fast because you’re not running JavaScript to handle it.

This would be ideal if, say, you wanted to poll for changes every second. If you did that using jQuery, you’d need to repeat $(“p”). That would execute a new document.getElementsByTagName(“p”) and copy the nodes into an array. That could be noticeably slower on large documents.

IT Mitică May 31, 2011 at 5:53 am

Please correct me if I’m wrong.

1. document.getElementsByTagName(“p”) returns a live NodeList, a type object, while HTMLCollection is an interface, which, to my understanding, it’s an object that offers methods and properties as a plus.

2. It’s live == internal, hidden re-evaluation on every iteration, or is there another mechanism, underneath? It’s an internal mechanism that automatically rebuilds the NodeList, which means it’s hidden from me, but executed on every call (pCollection.length, for example), nontheless.

3. To poll for changes more practical, you could use MutationEvent, but that leaves out IE8 and below.

IT Mitică May 30, 2011 at 2:00 pm

Never mind the previous, I’ve misunderstood.

IT Mitică May 30, 2011 at 1:41 pm

Even with
var pQuery = $(“p”)[0];
?

Phil May 30, 2011 at 11:24 am

A good point, I’ve seen a lot of this with code where, for instance, people create a jQuery object just to pull a value from a text field.

Alejandro May 30, 2011 at 10:25 am

I don’t think that’s entirely accurate:
- getElementsByTagName, getElementsByName and getElementsByClassName return a live NodeList.
- Things like document.forms, document.links, etc. those do return an HTMLCollection.
- There are also Static NodeLists.
http://alebelcor.blogspot.com/2011/03/htmlcollections-nodelists.html

Craig Buckler May 31, 2011 at 1:26 am

While the terminology is slightly different, NodeLists and HTMLCollections are indistinguishable when you’re programming in JavaScript.

However, it’s interesting to note that .querySelectorAll() is static. It returns all nodes in the document at the time it’s run and won’t update.

Alexander Trefz May 31, 2011 at 1:29 am

The querySelectorAll thing is pretty logical -> John Resig was one of the guys that proposed querySelectorAll, and its whole use is to get static arrays of this selector very fast, in other words: its made to help JS-Libraries.

Comments on this entry are closed.