Skip to main content
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Elijah Manor
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Elijah Manor ( @elijahmanor )

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

By on

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.

Want to use code from this post? Check out the license.

Reader Comments

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?

15,640 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.

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.

15,640 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.

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?

15,640 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.

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?

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!

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel