Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

jQuery Succinctly: Traversing With jQuery

by
Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

This post is part of a series called jQuery Succinctly.
jQuery Succinctly: jQuery Selecting
jQuery Succinctly: jQuery Manipulation

Difference Between find() and filter() Methods

The filter() method is used to filter the current set of elements contained within the wrapper set. Its usage should be left to tasks that require filtering a set of elements that are already selected. For example, the code below will filter the three <p> elements contained in the wrapper set.

<!DOCTYPE html>
<html lang="en">
<body>
    <p><strong>first</strong></p>
    <p>middle</p>
    <p><strong>last</strong></p>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {  // Alerts middle, by filtering out the first
      // and last <p> elements in the wrapper set.
      alert($('p').filter(':not(:first):not(:last)').text());
  })(jQuery); </script>
</body>
</html>

Notes: When using filter(), always ask yourself if it is absolutely necessary. For example, $('p').filter(':not(:first):not(:last)') could be written without filter()$('p:not(:first):not(:last)').

The find() method, on the other hand, can be used to further find descendants of the currently selected elements. Think of find() more like updating or changing the current wrapped set with new elements that are encapsulated within the elements that are already selected. For example, the code below will change the wrapped set from <p> elements to two <strong> elements by using find().

<!DOCTYPE html>
<html lang="en">
<body>
    <p><strong>first</strong></p>
    <p>middle</p>
    <p><strong>last</strong></p>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Alerts "strong"
      alert($('p').find('strong').get(0).nodeName);
  })(jQuery); </script>
</body>
</html>

Notes: You can actually combine the elements in the wrapper previous to using the find() method with the current elements by using andSelf() - e.g. $('p').find('strong').andSelf().

The concept to take away is that filter() will only reduce (or filter) the currently selected elements in the wrapper set while find() can actually create an entirely new set of wrapped elements.

Notes: Both find() and filter() are destructive methods that can be undone by using end(), which will revert the wrapped set back to its previous state before find() or filter() were used.


Passing filter() a Function Instead of an Expression

Before you run off and create a custom filter for selecting elements, it might make more sense to simply pass the traversing filter() method a function that will allow you to examine each element in the wrapper set for a particular scenario.

For example, let's say you would like to wrap all <img> elements in an HTML page with a <p> element that is currently not wrapped with this element.

You could create a custom filter to accomplish this task, or you could use the filter() method by passing it a function that will determine if the element's parent is a <p> element, and if not, then remove the element from the set before you wrap the <img> elements remaining in the set with a <p> element.

In the following example, I select every <img> element in the HTML page, and then I pass the filter() method a function that is used to iterate over each element (using this) in the wrapper set, checking to see if the <img> elements' parent element is a <p> element.

<!DOCTYPE html>
<html lang="en">
<body>
    <img>
    <img>
    <p>
        <img>
    </p>
    <img>
    <p>
        <img>
    </p>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      $('img').attr('src', 'http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif').filter(function () { return !$(this).parent('p').length == 1 }).wrap('<p></p>');
  })(jQuery); </script>
</body>
</html>

Notice that I am using the ! operator to change a Boolean value of true to false. This is because I want to remove <img> elements from the set that have <p> elements as their parent element. The function passed to the filter() method will only remove elements from the set if the function returns false.

The main point is that if you are dealing with an isolated situation, creating a custom filter-e.g. :findImgWithNoP-for a single situation can be avoided by simply passing the filter method a function that can do custom filtering. This concept is quite powerful. Consider what is possible when we use a regular expressions test in conjunction with the filter() method.

<!DOCTYPE html>
<html lang="en">
<body>
    <ul>
        <li>jQuery is great.</li>
        <li>It's lightweight.</li>
        <li>Its free!</li>
        <li>jQuery makes everything simple.</li>
    </ul>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function($){ 
      // Wrap a <strong> element around any text within
      // a <li> that contains the pattern "jQuery"
      var pattern = /jQuery/i;
      $('ul li').filter(function () { return pattern.test($(this).text()); }).wrap('<strong></strong>');
  })(jQuery); </script>
</body>
</html>

Traversing Up the DOM

You can easily traverse up the DOM to ancestor elements using the parent(), parents(), and closest() methods. Understanding the differences between these methods is critical. Examine the code below and make sure you understand the differences between these jQuery traversing methods.

<!DOCTYPE html>
<html lang="en">
<body>
    <div id="parent2">
        <div id="parent1">
            <div id="parent0">
                <div id="start"></div>
            </div>
        </div>
    </div>
 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>  (function ($) {
      // Alerts "parent0" x4
      alert($('#start').parent().attr('id'));
      alert($('#start').parents('#parent0').attr('id'));
      alert($('#start').parents()[0].id);
      // Gets actual DOM element
      alert($('#start').closest('#parent0').attr('id'));
      // Alerts "parent1" x4
      alert($('#start').parent().parent().attr('id'));
      alert($('#start').parents('#parent1').attr('id'));
      alert($('#start').parents()[1].id);
      // Gets actual DOM element
      alert($('#start').closest('#parent1').attr('id'));
      // Alerts "parent2" x4
      alert($('#start').parent().parent().parent().attr('id'));
      alert($('#start').parents('#parent2').attr('id'));
      alert($('#start').parents()[2].id);
      // Gets actual DOM element
      alert($('#start').closest('#parent2').attr('id'));
  })(jQuery); </script>
</body>
</html>

Notes: closest() and parents() might appear to have the same functionality, but closest() will actually include the currently selected element in its filtering.


closest()stops traversing once it finds a match, whereas parents() gets all parents and then filters on your optional selector. Therefore, closest() can only return a maximum of one element.


Traversing Methods Accept CSS Expressions as Optional Arguments

CSS expressions are not only passed to the jQuery function for selecting elements, but they can also be passed to several of the traversing methods. It might be easy to forget this because many of the traversing methods function without having to use any expression at all-e.g. next(). The expression is optional for the following traversal methods, but remember that you have the option of providing an expression for filtering.

  • children('expression')
  • next('expression')
  • nextAll('expression')
  • parent('expression')
  • parents('expression')
  • prev('expression')
  • prevAll('expression')
  • siblings('expression')
  • closest('expression')
Advertisement