Condividi tramite


DOM Traversal

The latest Platform Preview Build includes two great interoperable features for working with the DOM – DOM Traversal and Element Traversal. These features provide web developers with simple, flexible, and fast ways of traversing through a document using the same markup across browsers. These features come in the form of flat enumeration, simplifying the DOM tree to an iterative list, and filtering which enables you to tailor the set of nodes you traverse. These features work with the same markup across browsers – you can try out any of the code here in the IE9 platform preview and other browsers.

Without these features, finding an element of interest on a page requires you to do one or more depth-first traversals of the document using firstChild and nextSibling. This is usually accomplished with complex code that runs slowly. With the DOM and Element Traversal features, there are new and better ways of solving the problem. This blog post is a primer and provides a few best practices to get you on your way.

I’ll start with Element Traversal, since it’s the simplest of the interfaces and follows familiar patterns for enumerating elements in the DOM. Element Traversal is essentially a version of DOM Core optimized for Elements . Instead of calling firstChild and nextSibling, you call firstElementChild and nextElementSibling. For example:

 if (elm.firstElementChild)
{
    elm = elm.firstElementChild;
    
    while (elm.nextElementSibling)
    {
        // Do work...
    }
}

This is faster and more convenient, saving you the trouble of having to check for text and comment nodes when you’re really only interested in elements.

DOM Traversal is designed for much broader use cases. First, you create a NodeIterator or a TreeWalker. Then you can use one of the iteration methods to traverse the tree:

 var iter = document.createNodeIterator(elm, NodeFilter.SHOW_ELEMENT, null, false); // This would work fine with createTreeWalker, as well

var node = iter.nextNode();
while (node = iter.nextNode())
{
    node.style.display = "none";
}

The codepath above iterates through a flat list of all nodes in the tree. This can be incredibly useful since in many cases you don’t care whether something is a child or sibling of something else, just whether it occurs before or after your current position in the document.

A big benefit of DOM Traversal is that it introduces the idea of filtering, so that you only traverse the nodes you care about. While nodeIterator only performs flat iterations, TreeWalker has some additional methods, like firstChild(), that let you see as much or as little of the tree structure as you want.

The SHOW_* family of constants provides a way to include broad classes of nodes, such as text or elements (like SHOW_ELEMENT in the earlier example). In many cases, this will be enough. But when you need the most precise control, you can write your own filter via the NodeFilter interface. The NodeFilter interface uses a callback function to filter each node, as in the following example:

 

 var iter = document.createNodeIterator(elm, NodeFilter.SHOW_ALL, keywordFilter, false);

function keywordFilter(node)
{
   var altStr = node.getAttribute('alt').toLowerCase();
   
   if (altStr.indexOf("flight") != -1 || altStr.indexOf("space") != -1)
      return NodeFilter.FILTER_ACCEPT;
   else
      return NodeFilter.FILTER_REJECT;                
}

For a live example, check out my demo for DOM Traversal -- I used NodeFilter extensively there. Complex filtering operations on the list of media elements were as simple as using a NodeFilter callback like the one above.

In this post, I showed that you have options in how to traverse a document. Here are suggested best practices for when you should use the various interfaces:

  • If the structure of the document is important – and you’re only interested in elements – consider Element Traversal. It’s fast and won’t leave a big footprint in your code.
  • If you don’t care about document structure, use NodeIterator instead of TreeWalker. That way, it’s obvious in your code that you’re only going to be using a flat list. NodeIterator also tends to be faster, which becomes important when traversing large sets of nodes.
  • If the SHOW_* constants do what you need for filtering, use them. Using constants makes your code simpler, as well as having slightly better performance. However, if you need fine-grained filtering, NodeFilter callbacks become indispensable.

I’ve already found these features to be a great help in my own coding, so I’m really excited to see what you do with them. Download the latest Platform Preview, try out the APIs, and let us know what you think.

Thanks!
Jonathan Seitel
Program Manager

Comments

  • Anonymous
    January 01, 2003
    the IE9 Whether to support Array.valueOf()

  • Anonymous
    July 30, 2010
    The comment has been removed

  • Anonymous
    July 30, 2010
    The comment has been removed

  • Anonymous
    July 30, 2010
    The comment has been removed

  • Anonymous
    July 30, 2010
    Please stop with the "same markup" slogan.  JavaScript is not markup and it makes you look utterly clueless when you refer to it as such.

  • Anonymous
    July 31, 2010
    The comment has been removed

  • Anonymous
    July 31, 2010
    NodeFilter is still undefined in IE9PP3. Is that sample a sign that next PP/Beta will allow us to access Node constants ?

  • Anonymous
    July 31, 2010
    Just noticed that code in the demo :-) << if (!NodeFilter) { var NodeFilter = new Object(); NodeFilter.SHOW_ELEMENT = 1; NodeFilter.FILTER_ACCEPT = 1; NodeFilter.FILTER_REJECT = 2; NodeFilter.FILTER_SKIP = 3; } >> This is why IE's demos are working while samples hosted elsewhere were failing. You should have said it in the relaese notes, I think.

  • Anonymous
    July 31, 2010
    The CSS3 property ' Text-shadow:2px 2px 2px 2px; " is still not working in IE9 Platform preview 3. So please fix this one also for IE9.

  • Anonymous
    July 31, 2010
    I really hope IE9 supports bigger larger buttons.

  • Anonymous
    July 31, 2010
    The comment has been removed

  • Anonymous
    August 01, 2010
    The comment has been removed

  • Anonymous
    August 01, 2010
    "Mitch"-- It's not your place to say a feature is complete, particularly if you're trying to suggest that it's buggy. If it's buggy (and a bug filed on the issue hasn't been punted), then, by definition, it's not complete. Maybe things are different in France? There's nothing inherently wrong with declaring your variables at the top-- the point is that collapsing them into one statement is both unnecessary and likely to lead to confusion. When you're complaining about code samples, you should demonstrate clearly that you understand the difference between the requirements of the language and what are common coding style preferences.

  • Anonymous
    August 01, 2010
    @mitch74 > one VAR statement is allowed by function or program body I heard that in a webcast that gave a lot of other interesting generalizing advice like 'dont use bitwise operators because you dont need them' and 'multiplicative integer operations using bitshifting is not faster but probably slower than using multiplicative operators'. But i found out that the first is only true if you dont need them, and that the second is wrong. As for the variable declarations, i believe there are situations where the code 'is better' when the declarations are not at the beginning of the function or program body, e.g. the initializer expression of a three-expression 'for' loop. Or loop bodies and intermediate results. The speaker in that webcast also made some JSON - XML comparisons that revealed a fatal lack of  XML knowledge. He really should have been prepared better, especially for a beginners class webcast. I hope you didnt see the same webcast and follow all the speakers opinions without double checking and thinking for yourself. g

  • Anonymous
    August 01, 2010
    @Jim "Please stop with the "same markup" slogan.  JavaScript is not markup and it makes you look utterly clueless when you refer to it as such." YES. Not to mention the fact that you can't possibly engineer a single browser in a way that ensures that developers can write markup that will be rendered the same by the browsers you DON'T control. This was obviously the idea of some less-than-technical manager, and I'm sure the hard-working IE9 team is silently bearing the painful stupidity of this slogan. It makes about as much sense as Chewbacca living on Endor.

  • Anonymous
    August 02, 2010
    The comment has been removed

  • Anonymous
    August 02, 2010
    @NotLivingInMomsBasement Ah HA! It was a South Park reference to a Star Wars metaphor, which flags me as EXACTLY one of the hundreds of millions of people they're trying to reach, and your failure to publicly recognize that flags you as somebody who has no right to be flagging me as a... em... I don't know. Anyway, yes, they can attempt to adhere to other browsers' implementations IF there is a consensus, but they can do nothing for the cause of "same markup" if Opera, Firefox, and Safari are all handling something in different ways. The best they can manage in such a case is "Slightly Less Different Markup". Don't get me wrong, I applaud the efforts in the direction of coming back from Obscure Microsoft Island and joining the mainland where everybody else is, but it just sounds dumb for a browser team to be boasting that they are making their browser so that your HTML will work the same way in all browsers. It just doesn't make sense. Did I mention Chewbacca?

  • Anonymous
    August 02, 2010
    Kiddo, defending yourself as a "SouthPark-watcher" isn't going to win you a lot of credibility when someone accuses you of living in your parents' basement.

  • Anonymous
    August 02, 2010
    Huh? Oh, was he accusing me of that? I assumed that he was just adamantly declaring that HE did not live in his mother's basement. I completely missed that. Either way, where I live is irrelevant, and my credibility is not the issue. For the sake of argument, let's go ahead and say I live in TEN mother's basements... regardless of this, you either agree that "same markup" is a silly promise that a single browser maker can't deliver on, or you ascribe to some other belief. I, anonymous Internet commenter, and my illustrious reputation, neither add weight to nor subtract weight from my simple argument.

  • Anonymous
    August 02, 2010

  • CSS Transitions
  • CSS 2D Transforms
  • CSS Border Image Begging on both knees, IE Team.  Please make it happen!
  • Anonymous
    August 02, 2010
    The comment has been removed

  • Anonymous
    August 03, 2010
    Why does MSFT always find odd examples to use? If you are testing for the alt attribute, then you are looking for images... those images are already available in a DOM-L0 collection; document.images thus you can save piles on the traversal with: for(var i=0,iL=document.images.length;i<iL;i++){  //do tests... } or even document.getElementsByTagName('img'); You are surely not suggesting that developers should ignore the specs and add alt attributes to all kinds of other elements are you?

  • Anonymous
    August 03, 2010
    Writing examples that are understandable is to be commended. And ALT is a supported attribute on OBJECT, AREA, APPLET and INPUT in addition to IMG.

  • Anonymous
    August 09, 2010
    The comment has been removed