Doing DOM Traversal in jQuery

By now, we hope you’re convinced that the jQuery selectors offer a diverse set of options for creating a wrapper object with elements selected from the DOM. The next set of tools you’ll need in your arsenal will allow you to manage your wrapped set. Handling tasks such as getting element references, getting another wrapper object with a subset of the original, or generally just traversing the DOM are all handled with ease by jQuery.

Using the .find() method you can retrieve descendants from an already wrapped set of elements in a manner similar to selecting using the jQuery function. The .find() method also accepts a selector string. The net effect of using .find() is like updating the current wrapper set you’re operating on. In contrast to the jQuery function, the selector expression is required.

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script>

$(function(){

var wrapper = $(“ul”);

console.log(“selected list count:” + wrapper.length);

var newWrapper = wrapper.find(“#techBooks”);

console.log(“selected list count:” + newWrapper.length);

});

</script>

</head>

<body>

<ul id=”groceries”>

<li>Eggs</li>

<li>Bacon</li>

<li>Ham</li>

</ul>

<ul id=”books”>

<li>

<ul id=”techBooks”>

<li>Pro jQuery</li>

<li>JavaScript 101</li>

</ul>

<ul id=”mathBooks”>

<li>Calculus for the Gods</li>

<li>Intro to Finite State Machines</li>

</ul>

</li>

<li>How to Win at Jeopardy</li>

<li>How To Cook</li>

</ul>

</body>

</html>

Code snippet is from find.txt

In this example, the first console.log displays 4, whereas the second displays 1. The .find() method works for getting all of the descendants, but if you want to get only the first level of descendants use .children() instead.

jQuery also gives you several shortcuts for accessing special cases of a wrapped element set. It’s very common to want to retrieve the first and last elements of a wrapped set, and jQuery has precisely those shortcuts as the following code example demonstrates:

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script>

$(function(){

var listElements = $(“li”);

var firstEl = listElements.first();

var lastEl = listElements.last();

console.log(“firstEl value:” + firstEl.html());

console.log(“firstEl length:” + firstEl.length);

console.log(“lastEl value:” + lastEl.html());

console.log(“firstEl length:” + firstEl.length);

});

</script>

</head>

<body>

<ul>

<li>eggs</li>

<li>bacon</li>

<li>ham</li>

</ul>

</body>

</html>

Code snippet is from children.txt

Here you have an unordered list with three items: eggs, bacon, and ham. Using the jQuery function, all of the list items are selected, and then using the methods .first() and .last(), the corresponding wrapped elements are retrieved. To verify this, two console.logs are called displaying the length of each wrapper, which should be 1. You’ve probably noticed a call to the method .html(), which retrieves the content from each list item. More on this later.

You can, of course, specify a particular element to retrieve using the .eq() method. Like .first() and .last(), .eq() returns a wrapped object.

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>
</script>

<script>

$(function(){

var listElements = $(“li”);

var secondEl = listElements.eq(l);

var fourthEl = listElements.eq(3);

console.log(secondEl.html() + ” and ” + fourthEl.html());

});

</script>

</head>

<body>

<ul>

<li>eggs</li>

<li>bacon</li>

<li>ham</li>

<li>cheese</li>

<li>juice</li>

<li>sausage</li>

</ul>

</body>

</html>

Code snippet is from eq.txt

The console.log will show “bacon and cheese.”

Sometimes you may want to directly get a DOM node. For such cases you can use the .get() method, which accepts an index as its argument. get is zero-based, meaning that it begins its index count at zero, not one. The following code illustrates using .get() to access the underlying DOM element of members of the matched set.

<!DOCTYPE html>
<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script>

$(function(){

var listElements = $(“li”);

var secondEl = listElements.get(2);

var fourthEl = listElements.get(3);

console.log(secondEl.innerHTML + ” and ” + fourthEl.innerHTML); //”ham and cheese”

});

</script>

</head>

<body>

<ul>

<li>eggs</li>

<li>bacon</li>

<li>ham</li>

<li>cheese</li>

<li>juice</li>

<li>sausage</li>

</ul>

</body>

</html>

Code snippet is from get.txt

The method .get() accepts an index value and returns a DOM node; it’s possible to also perform the inverse operation with the method .index(). This method accepts a DOM element, and returns its index value. Just like .get(), .index() is zero-based.

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script>

$(function(){

var item2 = document.getElementById(“item2”);

console.log($(“li”).index(item2));

});

</script>

</head>

<body>

<ul>

<li id=”item0″>eggs</li>

<li id=”item1″>bacon</li>

<li id=”item2″>ham</li>

<li id=”item3″>cheese</li>

<li id=”item4″>juice</li>

<li id=”item5″>sausage</li>

</ul>

</body>

</html>

Code snippet is from index.txt

Not only can you pass a DOM element, but also a jQuery wrapper object. If the wrapper object passed in as an argument contains more than one element, then .index() returns the index of the first match.

A third type of argument that .index() accepts is a selector string. Again, if more than one element is returned, the zero-based index of the first match is returned. Both .get() and .index() are considered destructive because they break a jQuery chain. A chain is broken when the method invoked doesn’t return a jQuery wrapper object, and hence the chain can’t continue.

It’s useful to think of a chain as if it were a stack, where each item on the stack is a set of elements. Upon calling a method, such as .filter() or .find(), a new element set is pushed onto the stack. At times you might want to revert back to the original wrapper object. This is accomplished with the .end() method as the following example shows:

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script>

$(function(){

var listElements = $(“li”);

listElements

.filter(“:gt(3)”)

.css(“color”, “green”)

.end()

.filter(“:lt(3)”)

.css(“color”, “red”)

.end();

console.log( listElements.length );

});

</script>

</head>

<body>

<ul>

<li>eggs</li>

<li>bacon</li>

<li>ham</li>

<li>cheese</li>

<li>juice</li>

<li>sausage</li>

</ul>

</body>

</html>

Code snippet is from end.txt

Using .filter(), you get the last two list items from the original wrapper, listElements, and then using .css() you modify the text color to green. But, because you want to change every element with an index value less than 3 to red, a call is made to .end() returning the currently operated wrapper to the original set of list items. Notice the additional indentions made in the chain in order to make the code more readable.

Up to now, all the examples in this chapter have used the .length property to return the number of elements selected, but you can also use the .size() method as well. There is a performance disadvantage to using a method call instead of the length property, but if you prefer the method call for visual consistency with the rest of your code it’s there.

Occasionally you’ll need to convert the set of matched elements to a plain JavaScript Array. jQuery provides a method to do just that, called .toArray(). The following code sample illustrates this plainly named method in action. A list of names is selected using jQuery. It’s converted into a native Array and the Array method reverse() is used to swap the order of the items. Looping through the new version of the array and logging the names illustrates the successful result.

<!DOCTYPE html>

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js”>

</script>

<script >

$(function(){

var winners = $( “twinners li” ).toArray();

winners = winners.reverse();

for ( var i = 0, test = winners.length; i < test; i++) {

console.log(winners[i].innerHTML);

/*logs

Nicolas Frantz

Mark Cavendish

Andre Darrigade

Lance Armstrong

Andre Leducq

Bernard Hinault

Eddy Merckx

*/

}

});

</script>

</head>

<body>

<ol id=”winners” ><li>Eddy Merckx</li>

<li>Bernard Hinault</li>

<li>Andre Leducq</li>

<li>Lance Armstrong</li>

<li>Andre Darrigade</li>

<li>Mark Cavendish</li>

<li>Nicolas Frantz</li>

</ol>

</body>

</html>

Finally, there’s one more selection method to look at before moving onto modifying elements. .andSelf() is the kind of method that you don’t always need, but when you do need it it’s invaluable. As you learned when looking at .end() jQuery maintains a stack that keeps track of changes to the matched set of elements. When a method that traverses the DOM like .filter() or .find() is called the new matched set is pushed onto the stack. If you need to manipulate the previous set of matched elements as well, .andSelf() will come to the rescue. The following code sample illustrates using .andSelf() to add a div back onto the stack after doing a .find() manipulates the stack. The new set of matched elements matches the order of the elements in the DOM.

<!DOCTYPE html>
<html>

head>

<script src=’http://code.jquery.com/jquery-1.7.1.js’>
</script>
<script >

$(function(){

console.log( $(“div”) );
// [ div ] console.log( $( “div” ).find(“p”) );
// [ p ,p, p ] console.log( $( “div” ).find(“p”).andSelf() );
// [ div, p, p, p ]

});

</script>

</head>

<body>

<div>

<p>Paragraph</p>

<p>Paragraph</p>

<p>Paragraph</p>

</div>

</body>

</html>

Code snippet is from andSelf.txt

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *