Find the jQuery Bug #6: Traversing Trouble
12 Mar 2012Introduction
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: jQueryGet 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: jQueryReduce 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โฆ