Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Using jQuery to search Content and creating custom Selector Filters


Last week at Southwest Fox a number of people asked whether it’s possible to use jQuery to do selector searches based on element content. The basic functionality is provided through the :contains() filter function in jQuery which provide a case sensitive search of element content. If I want to find all elements in a set of table row that contain a certain bit of text I might do something like this:

$("#gdEntries>tbody>tr:contains(SQL)").addClass("selectionhighlight");

which in this case will highlight any row that contains the case sensitive SQL string:

HighlightCommands

Nice. This can be be pretty useful. For doing Interactive searches for example. If I add a dialog to the page and hook up a little bit of script code to fire on the keyup event I can dynamically select rows as you type matching the text typed into the textbox:

// Set up key search
$("#txtSearch").keyup(function() {
    $("#gdEntries>tbody>tr:not(:first-child):not(last-child)").removeClass("selectionhighlight");
    $("#gdEntries>tbody>tr:not(:first-child):not(last-child):contains(" + this.value + ")").addClass("selectionhighlight");
});

Talk about a cool effect. Try it out for yourself, it’s pretty neat how easy it is to search and select content interactively.

Got this idea from one of the plug-ins Dave Ward Posted, specifically jQuery quickSearch, but you can provide basic search similar to the plug-in with just these few lines of code.

Creating your own Custom Filters

One very cool feature about filters in the jQuery selector engine is that you can create your own. The :contains selector  works, but unfortunately the search performed is case sensitive. If you wanted to create a case insensitive version of :contains you can do so pretty easily either by replacing :contains or creating a new filter altogether.

Here’s a new filter implementation called :containsNoCase:

$.expr[":"].containsNoCase = function(el, i, m) {
    var search = m[3];
    if (!search) return false;
    return eval("/" + search + "/i").test($(el).text());
};  

A filter selector expression adds to the .expr object and an operator member (in this case the colon :). All filter expressions are defined on $.expr[“:”] as new functions (or strings that are eval’d). The function called receives 3 parameters: The matched element, an index of the element matched, and m which is an object that contains parse tokens as an array. element 0 is the full filter string including parameter, with token 1,2,3 being the :, filter name and parameter respectively. m[3] is the first parameter which in this filter is the string we’re searching for.

The code then uses an regEx to find the expression in the document using case insensitive matching and returns whether a match is found. The result should be true or false to indicate whether the element should be included in the jQuery matched set.

You can declare this code anytime after jQuery has been loaded in a page and then use it like this on the same code above:

$("#gdEntries>tbody>tr:containsNoCase(sql)").addClass("selectionhighlight");

Notice now I specify a lower case sql string which would have failed with plain :contains, but using :containsNoCase finds the match properly.

Here’s another useful filter I’ve used:

$.expr[":"].startsWith = function(el, i, m) {
    var search = m[3];        
    if (!search) return false;
    return eval("/^[/s]*" + search + "/i").test($(el).text());
};

which instead of finding all matched content only finds content that starts with a given string. This requires a more focused search to hopefully select individual elements rather than elements that contain other elements. In the example above I might want to find only the columns or the bold text inside of the columns to search for starting text:

$("#gdEntries>tbody>tr>td>b:startsWith(fix)").addClass("selectionhighlight");

But wait… there’s more.

While I was searching around today I also found out that you can extend the selection operators themselves. If I get really lazy and decide that :containsNoCase is really too wordy for my lazy typing fingers I can shortcut this by creating a custom selector operator, say $. The following code does just that:

$.expr[":"].containsNoCase = function(el, i, m) {
    var search = m[3];
    if (!search) return false;
    return eval("/" + search + "/i").test($(el).text());
};
jQuery.parse.push(/^\s*(\$)(\s*)(.*)$/);
jQuery.expr["$"] = jQuery.expr[":"].containsNoCase;

which lets you now call the code like this:

$("#gdEntries>tbody>tr $DevConnection").addClass("selectionhighlight");

Voila – custom query operator. You’ll want to be careful though not use operators that are already in use. $ works although I had worried that there would be overlap with the attribute ends with selector ([href=$_page.aspx]), but the two can co-exist. I suspect this has to do with the RegEx for the parse expression.

It’s pretty impressive what you can do with jQuery’s query engine and how extensible it is. If you’re interested how some of this works and what you can do with it, check out the jQuery source and look at how the base filters are implemented which gives a pretty intense glimpse of what’s possible with custom selectors.

Make Donation
Posted in jQuery  


Feedback for this Post

 
# re: Using jQuery to search Content and creating custom Selector Filters
by Duncan Smart October 24, 2008 @ 7:51am
That string concatenation smells all wrong - feels like there needs to be some encoding/escaping going on.

Also instead of
eval("/^[/s]*" + search + "/i")...
use:
re = new RegExp("pattern"[,"flags"]) instead
# re: Using jQuery to search Content and creating custom Selector Filters
by Rick Strahl October 24, 2008 @ 9:54am
@Duncan - yes you're right. There's a worry about expressing text properly escaped.

To be honest I've never figured out how to properly escape literals in RegEx and just now doing another search I still don't see a way to do this. Anybody have any pointers? How do you inject a literal string to match into a RegEx expression (in JavaScript) without worries about the literal causing RegEx escape sequences to trigger?

This is a start, but a bit ugly too:
http://simonwillison.net/2006/Jan/20/escape/
# re: Using jQuery to search Content and creating custom Selector Filters
by Martin October 24, 2008 @ 11:53am
Rick, you are a masteeeeeeeeer !!!
# re: Using jQuery to search Content and creating custom Selector Filters
by Vijay Santhanam October 24, 2008 @ 11:38pm
Rick, you've outdone yourself. This is excellent stuff. Had no idea you could create your own selector expressions. So useful!
# re: Using jQuery to search Content and creating custom Selector Filters
by Rick Strahl October 25, 2008 @ 12:09am
@Vijay - don't thank me. Thank the jQuery team for making a flexible tool like this that makes it so easy to extend the functionality that comes in the box.
# re: Using jQuery to search Content and creating custom Selector Filters
by Jeff Hubbard October 26, 2008 @ 6:00pm
The only solution to escaping regex literals is (unfortunately) the answer you already found: use more javascript/regex to escape them. Here's how I'd lay it out though:
String.prototype.escapeRegExp = function () {
    return this.replace(/[.*+?^${}()|[\]\/\\]/g, "\\$0");
};

Then you'd call it like so:
var myRE = new RegExp("hi\tthere".escapeRegExp(), "g");
// now myRE contains an escaped version of hi<tab>there as a regular expression


(side note: it's slightly annoying that your page requires javascript but doesn't provide any notice to that extent; I spent a bit re-filling in the security question only to finally figure out that NoScript had this page blocked by default)
# re: Using jQuery to search Content and creating custom Selector Filters
by Nitin May 21, 2009 @ 8:32pm
I want to sort Select tag data by value(not text) using Jquery.
# re: Using jQuery to search Content and creating custom Selector Filters
by ozzysong May 29, 2009 @ 2:00am
Is there a way to do the search "accent" insensitive?

I'm developing in Spanish and is a heavy accented language and I can't get it to work correctly.
# Creating custom Selector Filters
by jQuery Tutorials June 24, 2009 @ 7:23am
To understand more about custom jQuery selectors I would suggest reading this two posts:

Basics:
http://jquery-howto.blogspot.com/2009/06/custom-jquery-selectors.html

More advanced:
http://jquery-howto.blogspot.com/2009/06/jquery-custom-selectors-with-parameters.html
# re: Using jQuery to search Content and creating custom Selector Filters
by jplug November 30, 2009 @ 12:11pm
We're trying extract the value of a column 1 <TD> if column 0 :contains(SQL). Seems like the following should work but somethings not quite right. Obvious errors?

$("table tr:has(td:eq(0):contains(SQL)) td:eq(1)").text()

Thanks.
# re: Using jQuery to search Content and creating custom Selector Filters
by dhirendra January 15, 2010 @ 3:11am
Please tell me how to implement jquery with ajax request for search option, plz mail me the link of website, if do u kw any one for learning jquery quickly..

thanks
dhirendra
software engineer
# re: Using jQuery to search Content and creating custom Selector Filters
by Raf February 18, 2011 @ 10:53pm
For a long list elements to filter, you better hide-show, not only highlighted them.
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014