jQuery Mobile Example - Paged Search Results

This post is more than 2 years old.

A reader asked me earlier this week if I could show an example of search, with paged results, under the jQuery Mobile framework. I whipped up a simple demo in five or so minutes. This is not a testament to my coding ability, but rather to just how fracking cool jQuery Mobile is. Before I begin though so important disclaimers. This was built using jQuery Mobile Alpha 3. If you are currently wearing a jetpack then you are reading this in the future and should expect that the code I show here may not quite work the same in the final version of jQuery Mobile. Also - I've been using jQuery Mobile for all of one month. Take my "solution" with a huge grain of salt. Make that two grains just to be safe. Ok, enough with the disclaimers, let's get to work.

I began by creating a simple home page for my demo. (Note - I'm not going to cover every little detail of how jQuery Mobile works here - for that, please consult the docs.)

<!DOCTYPE html> <html> <head> <title>Search Art</title> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.css" /> <script src="http://code.jquery.com/jquery-1.5.min.js"></script> <script src="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.js"></script> </head> <body>

<div data-role="page" data-theme="e">

&lt;div data-role="header"&gt;
	&lt;h1&gt;Art Search&lt;/h1&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
	&lt;div data-inline="true"&gt;
	&lt;form action="search.cfm" method="post"&gt;
	&lt;input type="search" name="search" data-inline="true"&gt; &lt;input type="submit" value="Search" data-inline="true"&gt;
	&lt;/form&gt;
	&lt;/div&gt;
&lt;/div&gt;

</div>

</body> </html>

You can see I include the relevant libraries on top and my content is really just a simple form. The search form type isn't supported by all clients, but jQuery Mobile (jqm from now on) has unicorn magic and can make it work pretty much anywhere:

So - notice the form posts to search.cfm. Let's look at that.

<cfparam name="url.search" default=""> <cfparam name="form.search" default="#url.search#"> <cfparam name="url.start" default="1"> <cfset perpage = 10> <cfif len(trim(form.search))> <cfset search = "%" & trim(form.search) & "%"> <cfquery name="getart"> select artname, price, description from art where artname like <cfqueryparam cfsqltype="cf_sql_varchar" value="#search#"> or description like <cfqueryparam cfsqltype="cf_sql_varchar" value="#search#"> </cfquery> <cfelse> <cfset noSearch = true> </cfif> <cfif not isNumeric(url.start) or url.start lte 0 or round(url.start) neq url.start> <cfset url.start = 1> </cfif>

<div data-role="page" data-theme="e">

&lt;div data-role="header"&gt;
	&lt;a href="" data-rel="back"&gt;Back&lt;/a&gt;
	&lt;h1&gt;Search Results&lt;/h1&gt;
	&lt;a href="index.cfm" data-theme="b"&gt;Home&lt;/a&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
	&lt;cfif structKeyExists(variables,"noSearch")&gt;
		&lt;p&gt;
		Please &lt;a href="index.cfm"&gt;enter a search&lt;/a&gt; term.
		&lt;/p&gt;
	&lt;cfelseif getart.recordCount is 0&gt;
		&lt;cfoutput&gt;
		&lt;p&gt;
		Sorry, there were no results for #form.search#.
		&lt;/p&gt;
		&lt;/cfoutput&gt;
	&lt;cfelse&gt;
		&lt;cfloop query="getart" startrow="#url.start#" endrow="#url.start+perpage-1#"&gt;
			&lt;cfoutput&gt;
			&lt;p&gt;
			#currentrow# &lt;b&gt;#artname#&lt;/b&gt;
			#description#
			&lt;/p&gt;
			&lt;/cfoutput&gt;
		&lt;/cfloop&gt;

		&lt;div data-inline="true"&gt;
		&lt;cfoutput&gt;
		&lt;cfif url.start gt 1&gt;
			&lt;a href="search.cfm?search=#urlEncodedFormat(form.search)#&start=#max(1,url.start-perpage)#" data-role="button" data-inline="true"&gt;Previous&lt;/a&gt;
		&lt;cfelse&gt;
			&lt;!--- Didn't work... &lt;a href="" data-role="button" disabled data-inline="true"&gt;Previous&lt;/a&gt;---&gt;
		&lt;/cfif&gt;
		&lt;cfif url.start+perpage-1 lt getart.recordCount&gt;
			&lt;a href="search.cfm?search=#urlEncodedFormat(form.search)#&start=#url.start+perpage#" data-role="button" data-theme="b" data-inline="true"&gt;Next&lt;/a&gt;
		&lt;cfelse&gt;
			&lt;!--- See above...
			&lt;a href="" data-role="button" data-theme="b" disabled data-inline="true"&gt;Next&lt;/a&gt;
			---&gt;
		&lt;/cfif&gt;	
		&lt;/cfoutput&gt;
		&lt;/div&gt;		
		

	&lt;/cfif&gt;
&lt;/div&gt;

</div>

Ok, we've got a bit more going on here then before. The top portion handles my search. I'm using ColdFusion (of course), but any server side language would suffice. Scroll on down to inside the div with the data-role content. My first two IF blocks handle no search or no results. The final block handles outputting the results. For my pagination I used the same old code I've used in the past. The only difference is that I made use of JQM's ability to automatically turn links into buttons. For the most part this works really well. What did not work for me, and you can see it commented out above, was passing a "disabled" along. I probably could have simply used a 'grey' theme for my buttons. But for now I simply hid them. Here is an example:

Not bad, right? You can demo this here:

For the most part, I think you see that nothing special was done to make this work. JQM does so much of the work for you that I literally just had to use the right markup to get it to look pretty. If you test this in your browser, mobile or not, you will see my form post, and navigation, is all being done via Ajax. Do you see any JavaScript in my code? Any? Nope? That's right. jQuery Mobile is so powerful it could even defeat Chuck Norris probably. I decided to kick things up a notch though and work on a slightly sexier version...

This version was done using simple UL tags. You can view source to see it yourself. It's an incredibly small modification. The detail page you see took an additional 2 minutes of work.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Aaron Longnion posted on 2/26/2011 at 12:20 AM

Great that you're spreading the word about jQM. I'm loving it, too!

One small caveat is that jQuery Mobile Alpha 3 does not work in Internet Explorer (any version that I've tested), but I have read that they intend to support IE 8+ and IE Mobile on jQM Beta +

Comment 2 by andy matthews posted on 2/26/2011 at 12:50 AM

You should clarify "not working" Aaron. John Resig gave a presentation at Adobe MAX during which he explains their browser ranking system (A, B, C). It's all based around whether the browser supports CSS media queries.

The code in IE is functional, but might not "look" as good as more modern browsers due to the lack of support for media queries. That's the benefit of the progressive enhancement model that jQuery Mobile has, and IMO makes it better than Sencha since it's all pure HTML.

Comment 3 by Randy Heaps posted on 2/26/2011 at 2:00 AM

You have a recommended search that will return multiple values? Thanks!

Comment 4 by Raymond Camden posted on 2/26/2011 at 2:37 AM

@Randy: I'm sorry - but your question makes no sense. My search example returns multiple values.

Comment 5 by Randy Heaps posted on 2/26/2011 at 6:44 AM

Sorry not to be clearer... I had tried a couple of "real word" searches and hadn't found anything that would return a result so I could see something in action... Just tried a single letter and it work great.

Thanks for your examples... always something valuable.

Comment 6 by Aaron Longnion posted on 2/26/2011 at 8:20 AM

@Andy - in my testing, jQuery Mobile Alpha 3 returns a blank page in IE 8 & 9 when using jQuery 1.5, where 1.5.1 returns a pretty good version of jQM in IE 9. Are you not seeing the same?

Comment 7 by andy matthews posted on 2/26/2011 at 8:35 AM

Not at all. That's the whole point of progressive enhancement. Depending on how you're delivering the content, it's just HTML. Our mobile site, http://m.goba.mobi, even works on Blackberrys that can only serve plain text.

Do you have a link that you can share?

Comment 8 by SuperAlly posted on 2/26/2011 at 12:45 PM

Thanks very much Ray. Gave me a great start for something I wanted to try.

Comment 9 by Andrew Shepherd posted on 3/1/2011 at 10:52 PM

Do you have the a code snippet for the UL tags in the second, 'sexier', demo? I'm new to jQM and would like to check out how you ramped it up THAT much with just 'two minutes' more work!!

Comment 10 by andy matthews posted on 3/2/2011 at 7:34 AM

@andrew...

One of the only drawbacks of jQuery Mobile is that it can be tough to view source. This is because jQm is loading subsequent pages using AJAX, then replacing the contents of the current viewport with the new page.

If you use Firefox to view Ray's demo, then check the Net tab, you'll be able to see the HTML being sent over. Here's a single row of his results:

<ul data-role="listview">
<li>
<img src="artgallery/aiden01.jpg" />
<h3><a href="detail.cfm?id=1">charles1</a></h3>
<p>Pastels/Charcoal</p>
</li>
</ul>

Comment 11 by Andrew Shepherd posted on 3/2/2011 at 8:36 PM

@andy, Thanks for your reply. That helps!

Comment 12 by Rafael Ramirez posted on 3/7/2011 at 8:46 PM

First let me say this is my first post but not my first time reading your Blog, posts and conferences about Coldfusion. I'm in love with JQM and Coldfusion and right now I'm making my first JQM+CFM tests. I'm not an expert and perhaps this is easy but I'm not find why this happens.

If I create a simple JQM page with a data-role="navbar" in the header of the page, when I move to another page the bar extend to the bottom of the screen until the next page is display. I forgot to tell this is if the page is submitted as a .cfm page. The page is a pure html without any cfm in it to discard an y other problem. The same page work if not processed by coldfusion.

Any one know why is this happens and how to avoid this.

Thanks

PD
I try to include an example here but the system flagged my comment as spam.

Comment 13 by Raymond Camden posted on 3/7/2011 at 8:50 PM

Sorry my spam checker blocked the link. Can you try posting the link via bit.ly instead? To be clear, ColdFusion outputs HTML. Period. CF can't "break" jqm. But - it's possible something else is being output in your request by mistake. If you use a tool like Firebug, do you see something 'extra' being sent? Maybe CF debugging? That could impact your app.

Comment 14 by Rafael Ramirez posted on 3/7/2011 at 9:37 PM

Thanks for answer my post.
I know that coldfusion outputs HTML and that is the strange thing. When I open the source processed by CFM I do not see anything wrong.

If I open this same page as a .cfm I get the problem but the same page as .htm not.

I test this in firefox, safari and iphone and get the same.

This is a Bit.ly link to the .cfm page http://bit.ly/f0PnuJ

Thanks

Comment 15 by Rafael Ramirez posted on 3/7/2011 at 9:49 PM

This is a link at the source as a text file

Comment 16 by Rafael Ramirez posted on 3/7/2011 at 9:50 PM

OOPS. :-)

this is the link as a text file http://bit.ly/hQXDEo

Comment 17 by Raymond Camden posted on 3/7/2011 at 9:52 PM

Ah, I see it. The bar does go big. And you say if you use the same with .html it works perfectly?

(As a warning, I'm on vacation and am about to be away from the machine for a while.)

Comment 18 by Raymond Camden posted on 3/7/2011 at 9:53 PM

I see something bad. This is on top of the page

<title>- MobilPR Test -</title>

It is before the doctype which should be first. You need to stop outputting that.

Comment 19 by Rafael Ramirez posted on 3/7/2011 at 10:09 PM

Wow thanks I see this file and did not see that perhaps because I was so used to set that in the Application.cfm file.

Well YES that was the problem, I do not really know why but THANKS. You do not have an idea of how many times I pass through this source. LOL

Thanks for the help and happy vacations.

Comment 20 by Gerald posted on 5/5/2011 at 7:40 AM

Hi,
thanks for your great articles about jQuery Mobile. I've started to work on a mobile version of our site, so far so good!
I encountered a problem and I could not find anything online to give me a clue. Basically I have a listview of tags:
<ul data-role="listview"><li><a href="url-to-server-for-the-tag1">Tag 1</a></li>...(more tags)...</ul>
each tag has a url to get a list of corresponding articles from the server.
If I get as a result of this url an html mobile page there is no problem it works but what I would like is to get a JSON from our API and then use jQuery Template plugin to display them in a new page. I've been struggling with this for hours and could not find a clean solution yet. I could not find a clear answer on the net on the best way to do this. I can see jqm receiving the json from my server but then it is trying to add it as a page and I have no idea how to interrupt this...
Do you have any solution?
Thanks

Comment 21 by Raymond Camden posted on 5/5/2011 at 5:05 PM

So what you are saying is that you want the result of clicking a list item to be:

json info is loaded
result is printed on the page

I assume _below_ the list view, right?

Comment 22 by Gerald posted on 5/5/2011 at 6:11 PM

yep.
click on the link
json info is loaded
(jquery listview template is fill up and insert)
result is shown in a new page (not the same to get a back later)

Comment 23 by Raymond Camden posted on 5/5/2011 at 6:13 PM

Wait - you want to show the results on a new page? Then why are you bothering with json? Remember that JQM will load pages in via Ajax anyway to make things a bit zipper. I'm confused as to why you want to change the default behavior here as I don't see a gain.

Comment 24 by Gerald posted on 5/6/2011 at 1:12 PM

Yes I understand that all links are loaded through ajax but I have a REST API already available, I don't want to create new views only for the mobile version, I rather get the json and display it on the browser through jquery template plugin, it makes more sense to me and keep clear the separation.
Meanwhile I manage to make things work except that for the data-url part to make the history working properly. I am still trying to understand how jqm generate the data-url or I will simply create empty pages that I will feed with my data from the json, I think it will be easier.
sorry it's a bit confusing ;-)
Anyway, back to the ajax part, it looks strange to me to not being able to get json instead of html from jqm directly. I understand that html is usually good but when you want directly to use a json api, it does not work anymore.

Comment 25 by Raymond Camden posted on 5/7/2011 at 1:12 AM

So what I think you would want to do is simply register your own click events for those links. It should take priority over JQM.

Comment 26 by Raymond Camden posted on 5/7/2011 at 1:23 AM

So I just tested a list with 2 links.

<ul data-role="listview">
<li><a href="#page2">Page Two</a></li>
<li><a href="" id="testLink" rel="external">Test Link</a></li>
</ul>

and then I did:

<script>
$(document).ready(function() {

$("#testLink").click(function(e) {
alert("You clicked a link.");
e.preventDefault();
}
);

});
</script>

and I was able to correctly "take over" the link and do with it what I wanted.

Comment 27 by jlig posted on 10/31/2011 at 9:24 PM

Just now checking out your "paged search" code..
- and of course mine never works first-time!
- even though I copied everything exactly..

After trying several iterations, here is what I have currently
- http://cerberus.clearwave.c...
- When I click the search button (on my index.cfm page) I get the yellow "Error Loading Page" and the search.cfm page never displays at all?
- I tried to look at the Chrome Developer tools, but it gives back nothing valuable (that I can tell?)
- http://cerberus.clearwave.c...

Comment 28 by Raymond Camden posted on 10/31/2011 at 9:43 PM

What does the network tab tell you? Load the XHR request and look at the response.

Comment 29 by jlig posted on 10/31/2011 at 9:48 PM

It shows the following:
http://cerberus.clearwave.c...

Comment 30 by Raymond Camden posted on 10/31/2011 at 9:49 PM

Ok, but dude - click on it. :) If you click on it, you can see the response. It is an error (you can tell by the 500) in your CF code. Once you click you will see the error.

Comment 31 by jlig posted on 10/31/2011 at 9:52 PM

Ok, digging a bit deeper in the Network/Preview section:
- http://cerberus.clearwave.c...

Comment 32 by Raymond Camden posted on 10/31/2011 at 9:55 PM

The error is pretty clear. Your code references tblcust.name1. I assume you changed the code since the earlier link. If that was supposed to be the query then you should ensure the query name is tblcust.

Comment 33 by jlig posted on 10/31/2011 at 10:07 PM

Ray, you are correct.. and I also found this, where i failed to change your "getart" to my actual query name (getCust):
- <cfif url.start+perpage-1 lt getart.recordCount>

I changed it to:
- <cfif url.start+perpage-1 lt getCust.recordCount>
and it works correctly!

Once again, Thanks for the excellent code, & for pointing me to the Network/Preview screen.. I now know where to look to debug..

ps: Is there a missing "datasource=" in your code above (line 7): <cfquery name="getart">

Comment 34 by Raymond Camden posted on 10/31/2011 at 10:08 PM

In CF9, you can set datasource in the Application.cfc file. It applies to all queries in your application and saves you from typing it.

Comment 35 by jlig posted on 10/31/2011 at 10:28 PM

ah.. we are in process of turning up our new CF9 VM, but this week I only have our older CF7 to play with.. thanks for the tip..

Comment 36 by Nik posted on 3/6/2012 at 7:07 AM

Ray, do you have a code download for the second demo? I am particularly interested in the styling of search results page.

Comment 37 by Raymond Camden posted on 3/6/2012 at 9:58 AM

You want the front end code for that? You can just view source. :) If you want the ColdFusion side stuff, I can get you that.

Comment 38 by Nik posted on 3/6/2012 at 10:29 AM

I would like to have a look at the server side code, especially the formatting for the listview.

I have done something similar but my list was not formatting as shown in your example. I had a look at the source code to check which classes are used in the list tags (as you know some classes here are added dynamically by the jquery mobile library). Found out the class 'ui-btn-inner' was there along with other classes. I notice your source could only has ui-btn-inner. I am just curious to see why this is happening on my side.

Thanks
N

Comment 39 by Raymond Camden posted on 3/6/2012 at 10:38 AM

You can download the code here: http://www.raymondcamden.co....

Comment 40 by Nik posted on 3/7/2012 at 9:05 AM

Hi Ray. The link is giving a 404.

Comment 41 by Raymond Camden posted on 3/8/2012 at 12:13 AM

Change "downloads" to "enclosures" - sorry.

Comment 42 by jlig posted on 5/10/2012 at 8:28 PM

Ray, I notice that your Demo "saves" the original "search text" when I click the JQM back button.. or if I click the browsers Back button, but mine does not?

- I'm using a JQM back button to go back to the search page
- but mine "clears" out the original search text box?

Any ideas?

Comment 43 by Raymond Camden posted on 5/11/2012 at 3:13 PM

Sorry - no ideas. Is it online where I can see?

Comment 44 by jlig posted on 7/26/2013 at 7:32 PM

Ray, I'm looking to add "extra URL variables" to your search results page but getting zero results.. What needs to be changed in your original code to allow me to pass additional search criteria including the original search box?

- Here is what I have so far:
- Notice I have passed in 3 additional url form variables:
- I have also confirmed the 3 variables are passing to the page:
- I tried changing the OR's to AND's but still zero results:
--------------------------------------------------------
<cfparam name="url.CallType" default="">
<cfparam name="url.releaseReason" default="">
<cfparam name="url.connectedOnly" default="">
<cfparam name="url.search" default="">
<cfparam name="form.search" default="#url.search#">
<cfparam name="url.start" default="1">
<cfparam name="getMSCDR.recordcount" default="0">

<cfset perpage = 10>
<cfif len(trim(form.search))>
<cfset search = "%" & trim(form.search) & "%">
<cfquery name="getMSCDR" datasource="metaswitch_cdr">
SELECT CallType AS ct, ReleaseReason AS rr, CallingPartyAddr AS cpa, BusinessGroupName AS bgn, RequestedAddr AS ra, connected AS cc,
FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y %r') as rt, FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y') as rt2, FROM_UNIXTIME((ConnectTime/1000), '%a %m-%d-%Y %r') as cct, TO_DAYS(FROM_UNIXTIME((ReleaseTime/1000), '%a %m-%d-%Y %r'))-TO_DAYS(FROM_UNIXTIME((ConnectTime/1000), '%a %m-%d-%Y %r')) AS cl
FROM mscdr

WHERE

<cfloop index="x" from="1" to="#listLen(form.search, " ")#">
<cfset word = listGetAt(form.search, x, " ")>
<cfset word = "%" & ucase(word) & "%">
<cfif x neq 1>
and
</cfif>
(
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.CallType#">
or
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.releaseReason#">
or
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.connectedOnly#">

)
</cfloop>

</cfquery>
<cfelse>
<cfset noSearch = true>
</cfif>
<cfif not isNumeric(url.start) or url.start lte 0 or round(url.start) neq url.start>
<cfset url.start = 1>
</cfif>

Comment 45 by jlig posted on 7/26/2013 at 7:37 PM

fixed my typo: still zero results:
----------------------------------------
ucase(CallType) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.CallType#">
or
ucase(ReleaseReason) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.releaseReason#">
or
ucase(connected) like <cfqueryparam cfsqltype="cf_sql_varchar" value="#url.connectedOnly#">

Comment 46 by Raymond Camden posted on 7/26/2013 at 11:02 PM

Honestly I have no idea. Remember that the generated SQL is available if you turn on CF debugging. Try that - maybe it isn't what you think it is.

Comment 47 by Oghenez posted on 9/5/2013 at 12:33 PM

The download link for the source code is broken, can you re upload

Comment 48 by Raymond Camden posted on 9/5/2013 at 2:50 PM

Change "downloads" in the URL to "enclosures" and that will work.

Comment 49 by Oghenez posted on 9/5/2013 at 6:03 PM

thanks, i downloaded. pls hel me check if you can help here http://stackoverflow.com/qu...

Comment 50 by Raymond Camden posted on 9/5/2013 at 6:37 PM

I responded.

Comment 51 by F posted on 1/29/2014 at 2:35 PM

How would you accomplish this with multiple form fields (without having to pass them each individually in the url)?

Comment 52 by Raymond Camden posted on 1/29/2014 at 8:15 PM

You can store it in the Session scope.

Comment 53 (In reply to #0) by Raymond Camden posted on 7/9/2015 at 3:55 PM

Sorry for the delay - your email got buried. I believe I can send you a zip of the old code. If you want that, email me via the contact form so I have your info.