A few days back I was asked to build a scrolling div full of checkboxes populated via a database query record set that could be filtered. To accomplish this, I’ve often used AJAX in the past to hit a CF component that would run a SELECT statement containing a WHERE clause that looked for a record LIKE my existing value. This would return back the filtered item.
In this case however, I was working with a very large dataset and it was not appropriate to make an AJAX call to filter the dataset, and much less to do so on each key press to simulate live filtering. So I decided to filter on the client side and leave CF and AJAX out of the equation all-together. Naturally, I open the jQuery docs and start looking for something along the lines of a regular expression. As usual jQuery makes things even simpler than I could have imagined.
The jQuery selectors page shows multiple ways of selecting elements. One way is to use the ‘contains’ selector that essentially looks at the value of an attribute and checks if the substring exists in any of the available values. Once we know which elements are like and not like the substring (a.k.a. the filter criteria) we can use the jQuery show() and hide() functions to perform the filter visually for the user.
Let’s look at an example.
The HTML & Coldfusion
<!--- set a list of colors from which a set of checkboxes will be created --->
<cfset variables.colorsList = "Blue,Green,Red,Yellow,Orange,Black,White,Purple,Brown" />
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Color Filter</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<!--- create a filter text field --->
Filter Color:
<input type="text" id="filter" name="filterColor" value="" />
<br /><br />
<cfoutput>
<form name="colorForm" id="colorForm" method="post" action="##">
<!--- loop over the list of colors and output a checkbox for each --->
<cfloop list="#variables.colorsList#" delimiters="," index="variables.indexColor">
<div color="#lCase(variables.indexColor)#">
<label for="color_#lCase(variables.indexColor)#">
<input type="checkbox" id="color_#lCase(variables.indexColor)#" value="#lCase(variables.indexColor)#"/>
#variables.indexColor#
</label>
</div>
</cfloop>
</form>
</cfoutput>
</body>
</html>
As far as our HTML is concerned we have text field where we can type our filter criteria and a set of checkboxes for each color defined in the list ‘variables.colorsList’ at the top of the page. All of this is standard HTML, but do notice the custom attribute in the div tag called ‘color’. This attribute is included to give jQuery a value to filter on. So if I type ‘bl’ into the filter field jQuery will loop through each of the divs containing a checkbox, get the value of the ‘color’ attribute and check if the substring ‘bl’ is contained in the attribute value. So in this case it should hide all color checkboxes except ‘black’ and ‘blue’.
So let’s now look at the heart of this which is the jQuery.
The Javascript
<script type="text/javascript">
$(document).ready(function()
{
//focus the filter field on page load
$("#filter").focus();
//listen for the keyup event on the filter field
$("#filter").unbind().keyup(function()
{
//get the current filter input
var thisFilter = $(this).val();
//search through the color attribute assigned to each div containing the checkbox
//using the jQuery 'contains substring' selector (*=) and hide those that do not
//contain the substring match
$("div:not([color*='" + thisFilter + "'])").hide();
//now search for those that do match and show them
//this must come after the hide
$("div[color*='" + thisFilter + "']").show();
//lastly if the filter field is an empty string show all
if(thisFilter === "")
{
$("form div").show();
}
});
});
</script>
So, once again jQuery amazes me with the fact that, minus any var setting and event listening, client side filtering via showing and hiding can be accomplished in essentially two lines and a conditional that restores the data in the case that the filter is not being used. Incredibly simple!