Monday, March 12, 2012

Find the jQuery Bug #6: Traversing Trouble

Introduction


In this open-ended series I'll be showcasing a snippet of buggy jQuery code that you might encounter, explain what the problem is, and then identify how you can easily resolve the issue.

You can view other posts in this series...

The Desired Feature


We want to take the following list of jQuery board and team members and then hide only those that are team members, leaving only the board members showing.


The Buggy Code


The following code snippet is our first attempt at solving the problem, but there is a subtle error. Do you see it?


You can view, run, and edit the above code sample from jsFiddle.

The results that we expected was to only view a subset of the total list, but instead we ended up seeing all the items in the list!



The Underlying Problem


At the root of the problem is that the .find() method is used for finding elements that are descendants of the current jQuery collection.

.find( selector )
Returns: jQuery
Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.

-- http://api.jquery.com/find/

The above code snippet already starts with a jQuery collection of 22 items referenced by the $items variable. When calling the $item.find( ".team" ) method jQuery looks for all elements containing the team class that are children of it's internal collection. In this case, the list items do not have any children, so the result is an empty jQuery collection.

It is important to note that jQuery allows you to call methods off of any jQuery collection even if it is empty. The thing is that it just doesn't do anything, it silently fails. What we really need to solve this problem is to have some way to narrow down the internal jQuery collection based on a specified criteria. Thankfully, there is an easy way to do this.

A Solution


The solution to fix this problem is really simple and straightforward. The main problem is that we were using the wrong method.

We should have been using the .filter() method instead, which takes the current jQuery collection and filters them by matching against a provided selector. It doesn't traverse the children at all, but it's only purpose is to reduce the number of top level elements currently captured in the jQuery collection.

.filter( selector )
Returns: jQuery
Reduce the set of matched elements to those that match the selector or pass the function's test.

-- http://api.jquery.com/filter/

All you really need to do is to use the .filter() method instead of the .find() as we used in the previous example.


You can view, run, and edit the above code sample from JSBin.

If you test out the code again below you'll notice that the list items with class of team are targeted and hidden like we wanted!



Conclusion


The key concept to remember here is that the .find() method is for traversing into the DOM and locating descendants that match a criteria and the .filter() method is used to reduced the elements that are already selected that match a criteria.

This may seem like a trivial concept to grasp by some, but I've seen this common confusion of the two methods numerous times. I find that many developers expect that the .find() method will perform both filter and find, but it doesn't.

Until next time...

No comments:

Post a Comment