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

Posted September 25, 2009 at 10:07 AM

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:

 Launch code in new window » Download code as text file »

  • 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:

 Launch code in new window » Download code as text file »

  • $( "...." ).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:

 Launch code in new window » Download code as text file »

  • <!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:

 Launch code in new window » Download code as text file »

  • // 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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

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 »
6,516 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 »
6,516 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 »
6,516 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.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »
Nov 20, 2009 at 5:23 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Todd, Ahh, gotcha, yeah that makes sense. ... read »
Nov 20, 2009 at 5:17 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
Ben, sorry if I didn't make this clear. You can make it work like that if you want, just put <cfset session.foo = 1> (and <cfset application.foo = 1>) in your OnRequestStart() and it reve ... read »