Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Using jQuery to search Content and creating custom Selector Filters


:P
On this page:

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.

Posted in jQuery  

The Voices of Reason


 

Duncan Smart
October 24, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

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

Rick Strahl
October 24, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

@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/

Martin
October 24, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

Rick, you are a masteeeeeeeeer !!!

Vijay Santhanam
October 24, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

Rick, you've outdone yourself. This is excellent stuff. Had no idea you could create your own selector expressions. So useful!

Rick Strahl
October 25, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

@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.

Jeff Hubbard
October 26, 2008

# re: Using jQuery to search Content and creating custom Selector Filters

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)

Nitin
May 21, 2009

# re: Using jQuery to search Content and creating custom Selector Filters

I want to sort Select tag data by value(not text) using Jquery.

ozzysong
May 29, 2009

# re: Using jQuery to search Content and creating custom Selector Filters

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.

jQuery Tutorials
June 24, 2009

# Creating custom Selector Filters

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

jplug
November 30, 2009

# re: Using jQuery to search Content and creating custom Selector Filters

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.

dhirendra
January 15, 2010

# re: Using jQuery to search Content and creating custom Selector Filters

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

Raf
February 18, 2011

# re: Using jQuery to search Content and creating custom Selector Filters

For a long list elements to filter, you better hide-show, not only highlighted them.

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