Use jQuery and quickSearch to interactively search any data
AJAX, ASP.NET, UI, jQuery By Dave Ward on October 31st, 2008While reviewing my latest post’s outbound click-through stats, I noticed that most of you found Rik Lomas’ quickSearch more interesting than the rest. In light of that interest, I’ve decided to show you an example of how you might implement its functionality in conjunction with ASP.NET.
For this demonstration, I’m going to use Delicious as a source of sample data to apply the technique to. I’m sure you’re getting tired of my RSS feed as demo data, and the Delicious API provides an abundance of topical data via jQuery tag feeds.
In this post I will show you how to use LINQ to XML to query the Delicious API, display that data in a table, apply the quickSearch plugin to that table, work around a problem that arises when using quickSearch with ASP.NET, and lay the groundwork for a “no results” state.
Working with the Delicious API
The Delicious APIs are fairly straightforward to work with. Their XML API takes the shape of an RSS feed containing items matching a query that you specify within the request URL. This is sometimes referred to as a RESTful API.
For example:
That request would return an RSS document describing the 25 most recent items appearing on the “popular” list for the jQuery tag. The actual XML returned for each item would look something like this (slightly abridged):
<item> <title>Rich IntelliSense for jQuery</title> <pubDate>Wed, 29 Oct 2008 07:56:03 +0000</pubDate> <link>http://blogs.msdn.com/webdevtools/</link> <category>jquery</category> <category>ASP.NET</category> </item>
Now that we have a handle on how to request the data and what the data looks like, we can easily build a LINQ to XML expression to query the API.
Using LINQ to XML to query the API
If you’ve dealt with XPath for querying XML documents in the past, you’re going to love LINQ to XML. For example, these few lines of code are all that is required to execute the API request and extract a collection for databinding:
XDocument doc = XDocument.Load("http://feeds.delicious.com/v2/xml/popular/jquery"); var query = from item in doc.Descendants("item") select new { Title = item.Element("title").Value, Link = item.Element("link").Value, Tags = (from c in item.Elements("category") select c.Value).Take(5).ToList() };
This code will execute the REST request and then extract an IEnumerable collection containing the titles, URLs, and up to five tags for each item returned by the API.
Presenting the result of the API call
Presenting the data is a fairly standard affair. For this example, I’m going to use a table to display the results.
One detail we must attend to is that our table is rendered with a <tbody> element. Otherwise, it will be difficult to prevent the filtering functionality from applying to the table header as well.
To maintain the necessary control over the rendered HTML, let’s use a Repeater:
<asp:Repeater runat="server" ID="DeliciousItems"> <HeaderTemplate> <table id="DeliciousItems"> <thead> <tr> <th>Item</th> <th>Tags</th> </tr> </thead> <tbody> </HeaderTemplate> <ItemTemplate> <tr> <td><a href='<%# Eval("Link") %>'><%# Eval("Title") %></a></td> <td> <asp:Repeater runat="server" datasource='<%# Eval("Tags") %>'> <ItemTemplate><%# Container.DataItem %></ItemTemplate> <SeparatorTemplate>, </SeparatorTemplate> </asp:Repeater> </td> </tr> </ItemTemplate> <FooterTemplate> </tbody> </table> </FooterTemplate> </asp:Repeater>
The only tricky aspect of this is rendering the tags. Since those are contained in a List<string> nested inside each item’s Tags property, we need to nest a Repeater within the ItemTemplate to iterate over and display each item’s tags.
The trick to this is assigning the Tags property as the datasource for each of the nested Repeater controls. Using the SeparatorTemplate of a comma, we get a CSV rendering of the strings in the Tags List.
With a bit of CSS styling, our rendered table looks something like this:

Using quickSearch to implement progressive search
Now that we’ve built an appropriate table of data, using quickSearch to implement the progressive search functionality is the easiest step:
$(document).ready(function() { $("#DeliciousItems tbody tr").quicksearch({ // I don't care for the default label text. labelText: 'Search: ', // Anchor the search form to the table // and position it before the table attached: '#DeliciousItems', position: 'before', // React quickly to keypresses. delay: 100, // Eliminate the "loader". loaderText: '', }); });
It should make sense now why rendering the <tbody> was important. It provides us an avenue to more easily construct a jQuery selector which correctly identifies the DOM elements that we intend to identify as searchable content.
Most of the options are self explanatory. This page on the author’s site contains more information about those settings and their defaults.
If you’re new to jQuery, the position option may give you trouble at first. Position has four possible values which match corresponding jQuery manipulation functions: prepend, append, before, and after. See those documentation pages for examples of how each option works, relative to the attached element.
Note: When quickSearch is attached to a table, avoid using prepend or append. Inserting its form inside a table’s markup will cause bad things to happen.
Now, if only that had worked
There’s one big problem with all of this: it doesn’t work!
You probably know that ASP.NET WebForms controls, such as the Repeater used above, must be enclosed within a <form runat=”server”> element. However, what you may not have known is that the quickSearch plugin renders itself in a form tag as well.
Unfortunately for us, HTML simply does not allow nested form elements. So, if you try to render the quickSearch interface within a WebForm, things don’t go well.
Some browsers handle this nested form problem gracefully, but others don’t. In any case, you certainly should not rely on the browser correcting this for you.
Making it work
In this simple example, we could attach quickSearch above the WebForm’s <form> element and it would work. That isn’t a good general solution though.
So, I updated the plugin to render a <div> instead of a <form>. Problem solved.
The modified version is included in the source download below. Feel free to use it in your own projects.
After I wrote this, Rik was kind enough to add an option to the official plugin to fix this for us: isFieldset. Setting this option to true will cause the search form to be rendered as an HTML <fieldset> element, which solves our problem just as well as a <div> and is more semantically correct to boot.
Thanks, Rik!
Displaying a “no results” message
A commenter on my last post made a good point, mentioning that the plugin lacks support a “no results” message when your search string filters all elements.
By adding an onAfter event handler, we can easily add that functionality ourselves:
$("#DeliciousItems tbody tr").quicksearch({ labelText: 'Search: ', attached: '#DeliciousItems', position: 'before', delay: 100, loaderText: '', onAfter: function() { if ($('#DeliciousItems tbody tr:visible').length == 0) { // No Items. Do something fancy here. } } });
I’ll leave the presentation specifics up to you. Just don’t use <blink>!
Conclusion: worth the effort
When applied to the appropriate situation, this real-time filtering technique is very compelling. Your users will absolutely love it.
Also be sure to check out Rick Strahl’s recent post on highlighting search matches, instead of filtering them.
If you have a Delicious account, why not take a few seconds and save this post to Delicious. Maybe we can get this post on the popular list used in the demo!
Source
Similar posts
What do you think? Your comments are welcomed.
I appreciate all of your comments, questions, and other feedback, but please try to stay on topic. If you have a question unrelated to this post, I recommend posting on the ASP.NET forums or Stack Overflow instead.
If you're replying to an existing comment, please use the threading feature. To do this, click the "Reply to this comment" link underneath the comment you're replying to.

Comments
Great stuff,
I have been using this plugin and I noticed that for large gridviews say 500 rows that the search does slow down a lot not sure if you have encountered this?
Regards DotnetShadow
Just to let you know of an update, when I was using quicksearch for large tables I noticed a slow down, I came across this site:
http://www.west-wind.com/weblog/posts/519980.aspx
I implemented the easy way of doing filtering with jquery and noticed a huge improvement in speed
Regards DotnetShadow
Great follow-up. I was wondering why it didn’t use a div instead of a form as well. I’m curious though, in which browser did that break your page? It had still worked in all the ones I tested (FF3, Safari, IE6/7). XHTML Transitional…
It was intermittently going invisible on me in FF3 (when attached/positioned within the WebForms form).
This rather interests me. Do you know, Dave, if there’s any way to easily make it work with the Tablesorter plugin?
I haven’t tried those two together yet myself, but it ought to work. You may need to re-tablesorter the table in quickSearch’s onAfter event though.
I just tested it, and got to the point where I had the quicksearch running on the rows of the table. Unfortunately the way that the tablesorter plugin works, it hides the rows of the table that aren’t on the current page in the DOM, rather than simply setting them to not visible. So when you do the quicksearch you’re actually only doing it on the rows that Tablesorter is showing you rather than don the results as a whole.
So it’ll work… so long as you kill the paging functionality. This is proving to be true with the other options, I’m finding. If you could try it and point me in the right direction, It’d be awesome.
Very cool, thanks!
My apologies. It appears I accidentally killed the script include of tablesorter pager.
@Dave Ward : the ASP.NET Repeater does **NOT** need to be enclosed in a server side form (form runat=server thingie). Try it yourself.
The <form> troubles aren’t with the simple Repeater use case (as noted, you could just place the quickSearch UI outside the form in this simple example).
Helpfull blog.
@Jinno
@Dave
I messed around with combining tableSorter and quickSearch as well. Here is my take on it http://beckelman.net/post/2008/11/20/jQuery-tableSorter-and-quickSearch-Plugins-Together-Demo.aspx.
Regards - Bill Beckelman
Hi,
I’m trying to use quickSearch with a GridView and a DetailsView.
The grid and detailsview are in an UpdatePanel.
At first load, the search works. After i click on a link in grid and the details load, the search stops working.
Using a gridview and viewdetails wasn’t my choice.
Do you have any idea why it stops working?
I assume that because of the partial postback, but how to fix this, i don’t know.
Thanks,
Dan
Hi,
I’m not sure if my issue with quickSearch is a “feature” or I’m doing something wrong..
I use a gridView with 2 columns.
The search works fine, but when there is only value left matching the search text in the table, the plugin removes the second column (so I’m left with only one cell visible).
Is there any additional configuration needed in order to tell the plugin to always show the whole row?
Thank you