PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Eight Weeks of Prototype: Week 1, Beginning with Prototype

DOM Traversal Methods

In addition to being able to select elements in the DOM using $$() and select(), Prototype also allows you to select elements using the up(), down(), next() and previous() methods. These methods help you to easily find elements relative to a given element.

Unlike $$() and select(), each of these methods is used to retrieve exactly one element (if found). Because of this (and because each element is returned with the extended Prototype functionality), you can chain these calls together, as you will see at the end of this section.

The up() method

To find an element further up the DOM tree from a given element (that is, to find one of its ancestors) you can use the up() method. If no arguments are passed to up(), then the element's parent is returned.

If you don't just want an element's parent element but rather one of its other ancestors there are several different combinations of arguments that can be used. Firstly, you can specify a numerical index. For instance, using up(0) will retrieve the element's parent (the same as omitting the argument), using up(1) will return the element's grandparent, and using up(2) will return the great-grandparent.

Alternatively, you can pass a CSS selector to up(). The first matched element is returned. For example, if you have an image inside a HTML table (e.g. <table><tr><td> <img /> </td></tr></table>), you can use imgElt.up('table') to retrieve the table element (in this case using just imgElt.up() might return the <td> element instead).

You can also specify a numerical index along with a CSS selector. For example, if you have an image within two nested div elements, you can select the outer div by using imgElt.up('div', 1) (the first element from the target has an index of 0, which is the default value if the second argument isn't specified).

Listing 7 shows some examples of how to use the up() method to find an element's ancestors.

Listing 7 Examples of using up() to find an element's ancestors (listing-7.html)
<html>
    <head>
        <title>Examples of using up() to find an element's ancestors</title>
        <script type="text/javascript" src="/js/prototype.js"></script>
    </head>
    <body>
        <div id="main" class="foo">
            <table class="foo">
                <tr>
                    <td>
                        <a href="#"><img src="someImage.png" id="someImage" /></a>
                    </td>
                </tr>
            </table>
        </div>
        <script type="text/javascript">
            var img = $('someImage');
 
            // these are all equivalent ways of retrieving the link:
            var link = img.up();
            var link = img.up(0);
            var link = img.up('a');
 
            // similarly, there are several ways to retrieve the surrounding cell
            var cell = img.up(1);
            var cell = img.up('td');
 
            // the foo class is used in two different places
            var table = img.up('.foo');
            var div   = img.up('.foo', 1);
        </script>
    </body>
</html>

One of the most useful aspects of up() is that you can easily find an element without caring which elements lie between the element you want to find and the element you're searching on. That is, because you can use selectors to find the parent, you don't mind whether the element is the parent, the grandparent or otherwise.

For example, if you have a generic JavaScript class that relies on there being elements named in a particular way (by way of element IDs or class names), you don't have to worry about the specific structure of the elements in the DOM.

The down() method

The down() method is the opposite of the up() method, in that it searches within an element's descendants rather than in its ancestors. That is, it looks for elements within the target element.

Just like up(), you can either specify no arguments, a numerical index, a CSS selector, or a CSS selector with a numerical index. Specifying no arguments will result in the first child being returned.

Using down() is very similar to using the select() method covered earlier in this article, except that only a single element is returned using down() (remember that select() returns an array). Because of this, we can deduce that someElt.down('.foo') is effectively equivalent to someElt.select('.foo')[0].

The important difference is that trying to reference an particular element when using select() is that a JavaScript error will occur if the select() call returns an empty array. This is not an issue when using down().

Listing 8 shows some examples of using down() to find an element's descendants.

Listing 8 Selecting an element's descendants using down() (listing-8.html)
<html>
    <head>
        <title>Selecting an element's descendants using down()</title>
        <script type="text/javascript" src="/js/prototype.js"></script>
    </head>
    <body>
        <div id="main" class="foo">
            <table class="foo">
                <tr>
                    <td>
                        <a href="#"><img src="someImage.png" id="someImage" /></a>
                    </td>
                    <td>
                       Second cell
                    </td>
                </tr>
            </table>
        </div>
        <script type="text/javascript">
            var div = $('main');
 
            // there are several ways to find the table element
            var table = div.down();
            var table = div.down('table');
            var table = div.down('.foo');
 
            // you can specify an index to find a particular match
            var secondCell = div.down('td', 1);
 
            // complex selectors can be used
            var image = div.down('table.foo a img#someImage');
        </script>
    </body>
</html>

The next() and previous() methods

You can find sibling elements (that is, any element with the same parent element as the search target) using the next() and previous() methods. As suggested by their names, next() finds siblings elements that appear in the document after the search target, while previous() finds only siblings that appear before the search target.

The arguments used for next() and previous() work in the same manner as with up() and down(). That is, you can use a numerical index or a CSS selector.

Listing 9 shows several examples of using next() and previous().

Listing 9 Using next() and previous() to find sibling elements (listing-9.html)
<html>
    <head>
        <title>Using next() and previous() to find sibling elements</title>
        <script type="text/javascript" src="/js/prototype.js"></script>
    </head>
    <body>
        <div>
            <ul>
                <li id="first">First item</li>
                <li id="second">Second item</li>
                <li id="third">Third item</li>
                <li id="fourth">Fourth item</li>
                <li id="fifth">Fifth item</li>
            </ul>
        </div>
        <script type="text/javascript">
            var secondElement = $('first').next();
            var secondElement = $('third').previous();
 
            var thirdElement = $('third').previous().next();
 
            // this call will return null since #fifth is the
            // final child of the unordered list
            var nullElement = $('fifth').next();
        </script>
    </body>
</html>

Chaining traversal calls together

Because calls to up(), down(), next() and previous() each return a single element that has been extended with extra Prototype functionality, we can chain calls to these functions together.

For example, calling elt.down().up() will return the original element elt (note, however, that calling elt.up().down() will not necessarily return the original element; this will depend on the ordering of elements within elt's parent). Similarly, elt.next().previous() will also return elt.

Obviously there is little use for these examples in particular, however you may encounter situations where chaining these calls together is extremely useful. One such example might be to search all siblings of an element. Using elt.next(someSelector) only finds siblings before the given element, while elt.previous(someSelector) only finds siblings after the element. If you wanted to search either before or after, you could do so by using elt.up().down(someSelector).

Note: Depending on your CSS selector, this may also return the search target, not just its siblings. You may need to check for this in your code if this is a problem.

When chaining calls together, there is a risk that one of the later calls in the chain may cause an error due to an earlier call not returning an element (for instance, calling previous() on an element with no siblings will not return a valid element). Because of this, you should only chain your calls together when you know it cannot fail. Otherwise, you should make each call in a separate statement and check the return values accordingly.

In This Article