jQuery's Filter() Method Can Take A Callback Function For Advanced Filtering

Posted September 25, 2009 at 10:07 AM by Ben Nadel

Tags: Javascript / DHTML

I think one of the most awesome tips that I picked up while reading Cody Lindley's jQuery Enlightenment book was that the filter() method can take a callback function as its argument. Typically, when I use jQuery's filter() method, I simply pass in a selector that it would then use to narrow down the collection of DOM elements:

  • var items = $( "li" );
  • var visibleItems = items.filter( ":visible" );

In this tiny example, I am collecting all of the list items on the page; then, from that collection, I am creating a subsequent collection by filtering down to just the visible list items using the filter() method and the ":visible" pseudo selector.

The filter() method is awesome and has made life easy; but, knowing that the filter() method can accept a callback function for use with advanced filtering just made life some kind of exciting. Previously, if I needed to carry out advanced selecting, I might have created a custom jQuery pseudo selector; but, the filter() method just feels so much more straightforward and easy to use.

 
 
 
 
 
 
 
 
 
 

The callback function that you pass to the filter() method works, like most other jQuery callbacks, in the context of the current DOM element; meaning that within the callback function, the "this" keyword refers to the DOM element in question. When jQuery executes the callback functions, it passes in the index of the current DOM element (within the contextual collection) as the first argument:

  • $( "...." ).filter(
  • function( index ){
  • return( true | false );
  • }
  • );

Your callback function carries out the actual filtering by returning a boolean value. If you return true, the DOM element in question is included in the resultant collection; if you return false, the DOM element in question is excluded from the resultant collection.

To see this in action, I set up a small demo in which we can rate photos and then filter the list of photos based on a target rating:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>jQuery's Filter Method Used With A Callback</title>
  • <style type="text/css">
  •  
  • ul {
  • list-style-type: none ;
  • margin: 0px 0px 0px 0px ;
  • padding: 0px 0px 0px 0px ;
  • }
  •  
  • li {
  • border: 1px solid #999999 ;
  • float: left ;
  • height: 190px ;
  • margin: 0px 20px 5px 0px ;
  • padding: 5px 5px 5px 5px ;
  • }
  •  
  • </style>
  • <script type="text/javascript" src="../jquery-1.3.2.js"></script>
  • <script type="text/javascript">
  •  
  • // When the DOM is ready, populate the list.
  • $(function(){
  • var list = $( "ul" );
  • var listData = [];
  • var listTemplate = list.html();
  •  
  • // Create a list item for each of our photos.
  • for (var i = 1 ; i <= 6 ; i++){
  •  
  • // Create a list item.
  • listData.push(
  • listTemplate.replace( "$$id$$", i )
  • );
  •  
  • }
  •  
  • // Now that our array contains the raw HTML of our
  • // photo list, let's join that into a single chunk
  • // of HTML and inject it as the contents of our UL
  • // list.
  • list.html( listData.join( "" ) );
  • });
  •  
  •  
  • // When the DOM is ready, initialize the forms and
  • // the event listeners.
  • $(function(){
  • var targetRating = $( "form select:first" );
  • var list = $( "ul" );
  • var listItems = list.find( "> li" );
  •  
  • // I update the display based on teh selected rating.
  • var updateDisplay = function(){
  • // First, show all list items. Once all items have
  • // been shows, we will then hide the ones that do
  • // NOT match the current rating.
  • listItems.show();
  •  
  • // Now, check to see if we need to hid some of the
  • // list items. This will happen if the target
  • // rating has been selected (and is not zero).
  • if (parseInt( targetRating.val() )){
  •  
  • // Get all photos that don't match this rating.
  • // In order to do that, our filter() method is
  • // going to take a callback that checks the
  • // given photo's rating against the target
  • // rating the user selected.
  •  
  • listItems
  • .filter(function(){
  • var item = $( this );
  • var rating = item.find( "select" ).val();
  •  
  • // Return TRUE if this rating does NOT
  • // match the target rating.
  • return( rating != targetRating.val() );
  • })
  • .hide()
  • ;
  •  
  • }
  • }
  •  
  • // Now that we have our display update method defind,
  • // let's hook up each select box to trigger that
  • // method such that the display is always fresh.
  • $( "select" )
  • .change( updateDisplay )
  • .keyup( updateDisplay )
  • ;
  •  
  • // Call update display just to make sure we starte
  • // off with an accurate display.
  • updateDisplay();
  • });
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • jQuery's Filter Method Used With A Callback
  • </h1>
  •  
  • <form>
  •  
  • <p>
  • Please select rating:
  •  
  • <select>
  • <option value="0">Any</option>
  • <option value="1">1</option>
  • <option value="2">2</option>
  • <option value="3">3</option>
  • <option value="4">4</option>
  • <option value="5">5</option>
  • </select>
  • </p>
  •  
  •  
  • <h2>
  • The Photos
  • </h2>
  •  
  • <ul>
  • <!-- BEGIN: Template - will be replaced. -->
  • <li>
  • <img src="./$$id$$.jpg" width="125" /><br />
  •  
  • Rating:
  • <select>
  • <option value="1">1</option>
  • <option value="2">2</option>
  • <option value="3" selected="true">3</option>
  • <option value="4">4</option>
  • <option value="5">5</option>
  • </select>
  • </li>
  • <!-- END: Template - will be replaced. -->
  • </ul>
  •  
  • </form>
  •  
  • </body>
  • </html>

As you can see in the code above, there is one Select box at the top and one Select box associated with each photo. When any of the select boxes are changed, the display needs to be updated in order to keep the visible photos in sync with the target rating. To do so, our display method takes the list of photos and then filters them down, using the jQuery filter() method and a callback function, such that only photos with an inappropriate rating are hidden:

  • // Get all photos that don't match this rating.
  • // In order to do that, our filter() method is
  • // going to take a callback that checks the
  • // given photo's rating against the target
  • // rating the user selected.
  •  
  • listItems
  • .filter(function(){
  • var item = $( this );
  • var rating = item.find( "select" ).val();
  •  
  • // Return TRUE if this rating does NOT
  • // match the target rating.
  • return( rating != targetRating.val() );
  • })
  • .hide()
  • ;

If you ask me, this is totally awesome! This is one of those jQuery features that definitely changes the way you think about attacking display problems.




Reader Comments

Oct 29, 2009 at 6:29 PM // reply »
1 Comments

Great screencast! I was wondering...what if I have multiple criteria to filter for? So let's say, I wanted to filter by rating AND location (for example) with a second drop down option...

How would I go about doing this?


Oct 30, 2009 at 7:52 AM // reply »
11,314 Comments

@Adam,

The .filter() method simply has to return TRUE or FALSE. How you go about doing that is up to you. As such, you can easily compare more than a single drop down box within the filter() method and use multiple values to derive the true/false value you return.


Nov 16, 2009 at 1:05 AM // reply »
2 Comments

can i use this trick for double dropdown? or filter a second dropdown based on the selected item on the first dropdown? thx.


Nov 16, 2009 at 7:55 AM // reply »
11,314 Comments

@Neophyte,

Absolutely - the execution of the filter callback gives you the current DOM node context and the index; within that method, you can relate that to another select box in any way you like.


Nov 16, 2009 at 8:53 PM // reply »
2 Comments

thanks. we use hibernate and spring and jstl to populate the dropdown list. what we need to do is to select the cities (second dropdown) based on the selected state on the first dropdown. how do i go about doing that?


Nov 17, 2009 at 2:19 PM // reply »
11,314 Comments

@the_finisher,

For something like that, you're probably going to want to bind to the change() event on the select box and then make a subsequent call to the server for data.

Unless, are you pre-populating the OPTION list with ALL the cities? Or just the ones for the select city. It really depends on what is available currently on the page.


Sep 11, 2010 at 10:49 AM // reply »
1 Comments

If possible - does Jquery have the ability to read the # of images within your folder so that it could assemble an unordered list of the images within your page?


Sep 12, 2010 at 6:58 AM // reply »
11,314 Comments

@Jean,

jQuery doesn't have access to your file system.


Oct 1, 2011 at 10:30 AM // reply »
1 Comments

Ben, this is really awesome--exactly what I was looking for. Google searches often bring me to you, and I'm grateful you're putting such great examples on the web.

Thank you!


ned
Oct 14, 2011 at 10:09 PM // reply »
1 Comments

i am not even a coder but seems ima learn jquery :)) .. i got that idea of multiply filtering
(probably will ask stupidest question or non sense question but i gotta try its 5 am :)

3 levels filtering :
1. I(niche:carpets), II(location:uk), III(method:offline sale)
2. 1(persian carpets) , 2(london-park-17th-street-100001) ,3(no need of option here)
3. custom filtering for the niche-item:
-BLUE colored persian carpet
-sexy chick drawn on persian carpet

So its like lot of filters on the line probably should do separate internal filtering so i skip this in the main steps but i wonder if this is possible and also can it use this trackback function eventually :?? so when something change/new stuff appear it gets fixed...

I am really just asking in theory i doubt all this complication has any use right now but since i brainstormed whole night.. i had to ask :)
thx in advance for your reading & excuse me for my bad english.
Ned


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Jun 18, 2013 at 9:20 PM
Mapping AngularJS Routes Onto URL Parameters And Client-Side Events
I couldn't find examples of passing multiple arguments using the when() routing statement so figured out through trial and error that you can pass multiple arguments using the following format: .whe ... read »
Jun 18, 2013 at 3:39 PM
Experimenting With The Amazon Simple Storage Service (S3) API Using ColdFusion
Hi Ben, THANKS! While not bleeding edge, it is new to me & I like learning new things every day! ... read »
Jun 18, 2013 at 12:30 PM
Disabling Auto-Correct And Auto-Capitalize Features On iPhone Inputs
Also spellcheck="false" should be mentioned as part of html5 specs ... read »
Jun 18, 2013 at 8:40 AM
Using Named Functions Within Self-Executing Function Blocks In Javascript
Hi Ben, you forgot to mention the most important thing for named self-executing functions - they can be referenced by name ONLY inside their execution context (which is parens in this case), it mean ... read »
dee
Jun 18, 2013 at 7:01 AM
My Safari Browser SQLite Database Hello World Example
hai ben, this program is really good i could understand the concept but i dint know how to save it and how to open it as you have done in the video can u give that details pls ... read »
Jun 18, 2013 at 6:04 AM
Clearing Inline CSS Properties With jQuery
Thanks a lot for for post! It helped me a lot... after being stuck since 24 hrs.. found solution from your post. Thanks again! ... read »
Jun 18, 2013 at 2:31 AM
SOTR 2013 - The Best Conference I Never Went To
I keep watching it, should keep me happily distracted until SotR14 ;) ... read »
Jun 17, 2013 at 9:45 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, As I was reading what you wrote, it occurred to me that maybe I do something similar to that in some of my client-side code. In an application I'm working on, there are a bunch of unrelated ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools