WebReference.com - Chapter 17 of JavaScript: The Definitive Guide (4th Ed), from O'Reilly & Associates (14/15) | WebReference

WebReference.com - Chapter 17 of JavaScript: The Definitive Guide (4th Ed), from O'Reilly & Associates (14/15)

To page 1To page 2To page 3To page 4To page 5To page 6To page 7To page 8To page 9To page 10To page 11To page 12To page 13current pageTo page 15
[previous] [next]

JavaScript: The Definitive Guide (4th Ed)

Convenience Methods: The Traversal and Range APIs

So far in this chapter, we've discussed the core DOM API, which provides basic methods for document traversal and manipulation. The DOM standard also defines other optional API modules, the most important of which will be discussed in the chapters that follow. Two of the optional modules are essentially convenience APIs built on top of the core API. The Traversal API defines advanced techniques for traversing a document and filtering out nodes that are not of interest. The Range API defines methods for manipulating contiguous ranges of document content, even when that content does not begin or end at a node boundary. The Traversal and Range APIs are briefly introduced in the sections that follow. See the DOM reference section for complete documentation. The Range API is implemented by Netscape 6.1 (and partially implemented by Netscape 6), and the Traversal API is expected to be fully supported by Mozilla 1.0, which means that a future release of Netscape will support it. At the time of this writing, IE does not support either of these APIs.

The DOM Traversal API

At the beginning of this chapter, we saw techniques for traversing the document tree by recursively examining each node in turn. This is an important technique, but it is often overkill; we do not typically want to examine every node of a document. We instead might want to examine only the <img> elements in a document, or to traverse only the subtrees of <table> elements. The Traversal API provides advanced techniques for this kind of selective document traversal. As noted previously, the Traversal API is optional and, at the time of this writing, is not implemented in major sixth-generation browsers. You can test whether it is supported by a DOM-compliant browser with the following:

document.implementation.hasFeature("Traversal", 2.0)  // True if supported

NodeIterator and TreeWalker

The Traversal API consists of two key objects, each of which provides a different filtered view of a document. The NodeIterator object provides a "flattened" sequential view of the nodes in a document and supports filtering. You could define a NodeIterator that filters out all document content except <img> tags and presents those image elements to you as a list. The nextNode( ) and previousNode( ) methods of the Node-Iterator object allow you to move forward and backward through the list. Note that NodeIterator allows you to traverse selected parts of a document without recursion; you can simply use a NodeIterator within a loop, calling nextNode( ) repeatedly until you find the node or nodes in which you are interested, or until it returns null, indicating that it has reached the end of the document.

The other key object in the Traversal API is TreeWalker. This object also provides a filtered view of a document and allows you to traverse the filtered document by calling nextNode( ) and previousNode( ), but it does not flatten the document tree. TreeWalker retains the tree structure of the document (although this tree structure may be dramatically modified by node filtering) and allows you to navigate the tree with the firstChild( ), lastChild( ), nextSibling( ), previousSibling( ), and parentNode( ) methods. You would use a TreeWalker instead of a NodeIterator when you want to traverse the filtered tree yourself, instead of simply calling nextNode( ) to iterate through it, or when you want to perform a more sophisticated traversal, skipping, for example, some subtrees.

The Document object defines createNodeIterator( ) and createTreeWalker( ) methods for creating NodeIterator and TreeWalker objects. A practical way to check whether a browser supports the Traversal API is to test for the existence of these methods:

if (document.createNodeIterator && document.createTreeWalker) {
    /* Safe to use Traversal API */
} 

Both createNodeIterator( ) and createTreeWalker( ) are passed the same four arguments and differ only in the type of object they return. The first argument is the node at which the traversal is to begin. This should be the Document object if you want to traverse or iterate through the entire document, or any other node if you want to traverse only a subtree of the document. The second argument is a number that indicates the types of nodes NodeIterator or TreeWalker should return. This argument is formed by taking the sum of one or more of the SHOW_ constants defined by the NodeFilter object (discussed in the next section). The third argument to both methods is an optional function used to specify a more complex filter than simply including or rejecting nodes based on their type (again, see the next section). The final argument is a boolean value that specifies whether entity reference nodes in the document should be expanded during the traversal. This option can be useful when you're working with XML documents, but web programmers working with HTML documents can ignore it and pass false.

Filtering

One of the most important features of NodeIterator and TreeWalker is their selectivity, their ability to filter out nodes you don't care about. As described previously, you specify the nodes you are interested in with the second and (optionally) third arguments to createNodeIterator( ) and createTreeWalker( ). These arguments specify two levels of filtering. The first level simply accepts or rejects nodes based on their type. The NodeFilter object defines a numeric constant for each type of node, and you specify the types of nodes you are interested in by adding together (or by using the | bitwise OR operator on) the appropriate constants.

For example, if you are interested in only the Element and Text nodes of a document, you can use the following expression as the second argument:

NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT 

If you are interested in only Element nodes, use:

NodeFilter.SHOW_ELEMENT 

If you are interested in all nodes or do not want to reject any nodes simply on the basis of their types, use the special constant:

NodeFilter.SHOW_ALL 

And if you are interested in all types of nodes except for comments, use:

~NodeFilter.SHOW_COMMENT 

(See Chapter 5 if you've forgotten the meaning of the ~ operator.) Note that this first level of filtering applies to individual nodes but not to their children. If the second argument is NodeFilter.SHOW_TEXT, your NodeIterator or TreeWalker does not return element nodes to you, but it does not discard them entirely; it still traverses the subtree beneath the Element nodes to find the Text nodes you are interested in.

Any nodes that pass this type-based filtration may be put through a second level of filtering. This second filter is implemented by a function you define and can therefore perform arbitrarily complex filtering. If you do not need this kind of filtering, you can simply specify null as the value of the third argument to create-NodeIterator( ) or createTreeWalker( ). But if you do want this kind of filtering, you must pass a function as the third argument.

The function should expect a single node argument, and it should evaluate the node and return a value that indicates whether the node should be filtered out. There are three possible return values, defined by three NodeFilter constants. If your filter function returns NodeFilter.FILTER_ACCEPT, the node is returned by the NodeIterator or TreeWalker. If your function returns NodeFilter.FILTER_SKIP, the node is filtered out and is not returned by the NodeIterator or TreeWalker. The children of the node are still traversed, however. If you are working with a TreeWalker, your filter function may also return the value NodeFilter.FILTER_REJECT, which specifies that the node should not be returned and that it should not even be traversed.

Example 17-10 demonstrates the creation and use of a NodeIterator and should clarify the previous discussion. Note, however, that at the time of this writing none of the major web browsers support the Traversal API, so this example is untested!

// Define a NodeFilter function to accept only <img> elements
function imgfilter(n) {
    if (n.tagName == 'IMG') return NodeFilter.FILTER_ACCEPT;
    else return NodeFilter.FILTER_SKIP;
}
 
// Create a NodeIterator to find <img> tags
var images = document.createNodeIterator(document,  // Traverse entire document
    /* Look only at Element nodes */     NodeFilter.SHOW_ELEMENT,
    /* Filter out all but <img> */       imgfilter,
    /* Unused in HTML documents */       false);
 
// Use the iterator to loop through all images and do something with them
var image;
while((image = images.nextNode(  )) != null) {
    image.style.visibility = "hidden";  // Process the image here
}

Example 17-10: Creating and using a NodeIterator


To page 1To page 2To page 3To page 4To page 5To page 6To page 7To page 8To page 9To page 10To page 11To page 12To page 13current pageTo page 15
[previous] [next]

Created: November 28, 2001
Revised: November 28, 2001

URL: http://webreference.com/programming/javascript/definitive/chap17/14.html