jQuery Depth-First DOM Walking

posted on Thursday, October 1, 2009 at 6:43 AM PDT by Slippy Douglas | 0 comments

In my recent exploration of jQuery, I came across the need to walk the DOM tree, depth-first.

Ad-Hoc

After playing around in FireBug, I came up with an ad-hoc soltution that involves:

  • initially: setting the starting node
  • then: toggling between:
    • displaying information about it (I was doing a .offset() to figure out where my element positions were going fractional)
    • walking to the first child if it exists; if not, then walking to the next sibling if it exists; if not, then walking to the first ancestor which is not the last sibling

The initial code I entered into FireBug for it looked like this:

// sets the first node
var node = $('body');
// walks to the next element and spits it and it's offset out;
// repeat as many times as you want (until the node is "[]" and the offset is "0, 0")
[
        node = (node.children(':first')[0]) ?
                node.children(':first') :
                ( (node.next()[0]) ?
                        node.next() :
                        node.closest(':not(:last-child)').next()
        ),
        node.offset()
];

And it worked! From my tests, it walks each node, as expected.

jQuery-ized

Then I got to thinking. This would be useful all over. I could… make it a jQuery-ized function! So I tried this:

jQuery.fn.walk = function() {
        var firstChild = this.children(':first');
        if (firstChild[0]) {
                return firstChild;
        } else {
                var nextSibling = this.next()
                if (nextSibling[0])
                        return nextSibling;
                else
                        return this.closest(':not(:last-child)').next();
        }
}

Then you just walk from the initial element node like this:

node = node.walk();

Super jQuery-ized

Once I had this working, I thought, “I can do better still”. After poking around on John Resig’s blog a bit, and playing with different ways of allowing an expression for an argument, I realized that this is either going to be really slow (by grabbing all the elements in the document and finding the next logical one) or really really slow (by using recursion in a fashion similarto above). It might be plausible with full XPath, but I just don’t know enough about Sizzle to make it work efficiently.

Perhaps someday, though! For now, it’s pretty easy to just put the walk in a loop until you get the element type you’re looking for. Best of luck and be sure to correct me if I’m doing this “the hard way”!

Post a comment


(required, but not displayed)

(optional, and awesome)

(required)