A little while ago a nightly of Firefox 3.1 included support for the new Element Traversal API proposed by the W3C.
The purpose of this proposal is to make it easier for developers to traverse through DOM elements without having to worry about intermediary text nodes, comment nodes, etc. This has long been a bane of web developers, in particular, with cases like document.documentElement.firstChild
yielding different results depending on the whitespace structure of a document.
The Element Traversal API introduces a number of new DOM node properties which can make this traversing much simpler.
Here’s a full break-down of the existing DOM node properties and their new counterparts:
Purpose | All DOM Nodes | Just DOM Elements |
---|---|---|
First | .firstChild | .firstElementChild |
Last | .lastChild | .lastElementChild |
Previous | .previousSibling | .previousElementSibling |
Next | .nextSibling | .nextElementSibling |
Length | .childNodes.length | .childElementCount |
These properties provide a fairly simple addition to the DOM specification (and, honestly, they’re something that should’ve been in the specification to begin with).
There is one property that is conspicuously absent, though: .childElements
(as a counterpart to .childNodes
). This property (which contained a live NodeSet of the child elements of the DOM element) was in previous iterations of the specification but it seems to have gone on the cutting room floor at some point in the interim.
But all is not lost. Right now Internet Explorer, Opera, and Safari all support a .children
property which provides a super-set of the functionality that was supposed to have been make possible by .childElements
. When support for the Element Traversal API was finally landed for Firefox 3.1, support for .children
was included. This now means that every major browser will support this property (far in advance of all browsers supporting the rest of the true Element Traversal specification).
I think that the Element Traversal spec is missing a huge opportunity here to specify something that has become a de facto standard amongst browsers. Maybe it’ll make the second version of the Element Traversal spec, heh.
There are two big points that need to be explored here:
- Now that the
.children
property is virtually everywhere how can we start to use it to simplify our code? - Can we use
.children
, or parts of the Element Traversal API, to help speed up existing code?
To answer this question I mocked up a quick little plugin for jQuery that replaces the internals of the existing .prev(), .next(), .prevAll(), .nextAll(), .siblings(), and .children() methods with .children and the Element Traversal API methods.
The resulting code is absolutely simpler – previously there were numerous checks to see if a Node was, or was not, a DOM element – which resulted in lots of extra kludgy methods to handle those cases. But was the code faster?
I plugged the code into Dromaeo to see if there was any speed up in Firefox 3.1. The result? There is no discernible speed improvement to using the new DOM Traversal methods (.firstChildElement, etc.). This isn’t, necessarily, a bad thing – we just got the same performance that we see now but with a better API.
However there is a large improvement in speed when using .children (for the .siblings() and .children() jQuery methods). With this addition .siblings() is 84% faster and .children() is 35% faster. Considering that the .children method is now available in all browsers it’s making a lot of sense for people to get on board and start using it in their code bases for a definite hot path to extra performance. (Although, this is definitely not a new revelation – with frameworks like Dojo having used .children in their selector code for quite some time now.)
If nothing else the argument to having a simple branch in your code to handle using .children
is absolutely becoming more compelling.
James (November 10, 2008 at 1:06 pm)
Very interesting, I had no idea that such an API was even in development. You’re right, it would’ve made sense for this to be a part of the specification to begin with. Those sound like some impressive speed improvements with siblings() & children() – can we assume this will be integrated into a near-future release of jQuery?
Nice post John. :)
John Resig (November 10, 2008 at 1:08 pm)
@James: I’m certainly going to play around with it – the performance improvements look promising and there really isn’t much new code overhead so it might make sense to just land it.
Edward (November 11, 2008 at 4:47 am)
The specification uses other set of names instead of mentioned:
interface ElementTraversal
{
readonly attribute Element firstElementChild;
readonly attribute Element lastElementChild;
readonly attribute Element previousElementSibling;
readonly attribute Element nextElementSibling;
readonly attribute unsigned long childElementCount;
};
John Resig (November 11, 2008 at 10:11 am)
@Edward: Thanks for catching that, that was a mistake on my part. I’ve fixed the blog post.
funtomas (November 11, 2008 at 11:40 am)
Is Dromeo going to reflect and incorporate this DOM trav. method set?
LX (November 11, 2008 at 12:02 pm)
That looks interesting, though I hope there will be a fallback in jQuery for older browsers. I like the approach jQuery is taking – to do simple yet useful things fast rather than adding complexity.
John Resig (November 11, 2008 at 6:01 pm)
@funtomas: Maybe? Although there’s only one browser that supports it right now, not much purpose to adding more tests for it, in.
@LX: Why wouldn’t there be a fallback? All that’s being provided in the plugin that I showed above is a way for newer browsers to take advantage of the new methods – we’ll always provide fallback methods for the older browsers that we still support – don’t worry!
Doug Schepers (November 11, 2008 at 10:08 pm)
Yup, this and (a bit) more will be included in “Element Traversal 2: The Ennodelisting”. There were funky legacy reasons to leave it out of v1, but I’ve already got the spec started for the next version. Since there’s already support, and tests are super-simple to write for this, I would expect it to be a Rec in Q2-Q3 2009.
Tino Zijdel (November 12, 2008 at 10:41 am)
It looks like your plugin wil always return an empty array for .children()