Collapsible content and Ajax loading with jQuery Mobile

This post is more than 2 years old.

John sent me an interesting question. He wants to make use of jQuery Mobile but needed a way to get sets of content on a page that would load only when requested. He tried using a list view but this failed to render properly for his needs. Turns out - there are all kinds of nice gems within jQuery Mobile, one of them being collapsible content items.

They are - like most things in jQuery Mobile - incredibly simple to use. Just wrap your content in a specially marked div and you get a collapsible item:

<div data-role="collapsible">   
    	<h1>Item One</h1>
    	<p>
    	This is my content.
    	</p>
</div>

And that's it. Really. If you want to make the content default to collapsed, you have to write a bunch of JavaScript code... nah, I'm kidding, you just add an argument:

<div data-role="collapsible" data-collapsed="true">   
    	<h1>Item One</h1>
    	<p>
    	This is my content.
    	</p>
</div>

Ok, so that's incredibly trivial. What isn't so trivial is how to make the actual content something you load via Ajax. The docs don't discuss this, but a Google search turned up this forum posting that says an event is fired when your div is collapsed or expanded. The example they provide is this:

$('div').live('expand', function(){
    console.log('expand');
  }).live('collapse', function(){
    console.log('collapse');
});

Easy enough, right? So I whipped up a super simple static front page:

<!DOCTYPE html> 
<html> 
	<head> 
	<title>Page Title</title> 
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.css" />
	<script src="http://code.jquery.com/jquery-1.5.2.min.js"></script>
	<script src="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.js"></script>
	<script>

	$('div.info').live('expand', function(){
		//get the ID for this record
		var record = $(this).data("record");
	    console.log('expanded '+record);
		$(".detail", this).load("test2.cfm?record="+record);
	  });

	</script>
</head> 
<body> 

<div data-role="page">

	<div data-role="header">
		<h1>Page Title</h1>
	</div>

	<div data-role="content">	

		<div data-role="collapsible" data-collapsed="true" class="info" data-record="1">   
	    	<h1>Item One</h1>
			<div class="detail"></div>   
	    </div>

		<div data-role="collapsible" data-collapsed="true" class="info" data-record="2">    
	    	<h1>Item Two</h1>    
			<div class="detail"></div>  
	    </div>
	
	</div>

	<div data-role="footer">
		<h4>Page Footer</h4>
	</div>

</div>

</body>
</html>

I've got two collapsible items here - hard coded. Normally this would probably be database driven. Note that I've applied a class to them as well as a data item called record. If you go up to the JavaScript, you can see I've modified the selector to be more specific. It only picks up divs with the info class. I also only care about expansion so I didn't bother with a collapse handler. Once one of my divs is opened, I grab the record value (imagine this is a database table primary key) and call a load event. test2.cfm isn't doing anything interesting but here it is anyway:

<cfparam name="url.record" default="">

<cfoutput>
This is dynamic data for record #url.record#
</cfoutput>

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 JP posted on 4/6/2011 at 2:31 AM

Thanks Ray, I got it working on my jquery mobile test site:

http://m2.jhweather.com/ind...

I still have a lot of CSS to fix up... speaking of which, do you know why the content of each list element doesn't span the total width of the list? It only spans about 3/4 of the list width.

Comment 2 by Rick Mason posted on 4/6/2011 at 3:08 AM

Thought you linked to the wrong demo for a second there Ray ;<). In IE-8 selecting either choice gives you a custom 403 message. Works as intended in Firefox.

Comment 3 by JP posted on 4/6/2011 at 3:26 AM

Just fixed a couple jqplot errors and got my example working in IE 8:

http://m2.jhweather.com/ind...

Click on "View 24 Hour History"

I wonder if it's possible to get jQuery to NOT do a re-load after you've already expanded.

Comment 4 by Raymond Camden posted on 4/6/2011 at 4:22 AM

@Rick: It's the console message. If you remove it, it should work fine in IE for you. Yes - I made _that_ mistake again. ;)

Comment 5 by Raymond Camden posted on 4/6/2011 at 4:23 AM

@Rick: Removed the console.log just for you.

Comment 6 by Raymond Camden posted on 4/6/2011 at 4:24 AM

@JP: Ah, good catch there. It should NOT reopen. Give me 10 minutes.

Comment 7 by Raymond Camden posted on 4/6/2011 at 4:28 AM

Ok, check out

http://www.coldfusionjedi.c...

All I did was add a simple flag per record:

var done = {};

$('div.info').live('expand', function(){
//get the ID for this record
var record = $(this).data("record");
if (!done[record]) {
$(".detail", this).load("test2.cfm?record=" + record);
done[record] = 1;
}
});

Comment 8 by JP posted on 4/6/2011 at 4:47 AM

Nice... I added it to my page:

http://m2.jhweather.com/ind...

Now I just need to make it all look cool. Time to dive into navigation lists.

Comment 9 by Drew Wells posted on 4/7/2011 at 6:11 PM

Good article, have you seen this? "Why you should never use jquery live" http://jupiterjs.com/news/w...

Comment 10 by Raymond Camden posted on 4/7/2011 at 6:13 PM

I had not - and thank you. This is something I had kind of heard before - but getting out of the habit has been hard to do. (Of course, the original code was taken from the jQuery forums - so may I blame them instead? ;)

Comment 11 by Drew Wells posted on 4/7/2011 at 8:32 PM

Yes you can! Maybe this is why Stackoverflow allows their uber users to edit other users content.

Comment 12 by JP posted on 4/7/2011 at 10:15 PM

I tried using delegate (), but I can't seem to get it to work.

Comment 13 by Raymond Camden posted on 4/7/2011 at 10:17 PM

Maybe share a pastebin link with your code?

Comment 14 by JP posted on 4/7/2011 at 10:25 PM

http://pastebin.com/Mzb73bjr (doesn't include the test2.cfm file it's trying to load)

But first, I need to get the delegate function to bind to the expand action.

Comment 15 by JP posted on 4/7/2011 at 11:35 PM

I can get it working with:

$('*').delegate ('.info', 'expand', function (event) {...}

not sure why it won't work with:

$('.weather-station').delegate ('.info', 'expand', function (event) {...}

I had thought that delegate finds the specified nodes inside the initial selector.

Comment 16 by Steve posted on 7/7/2011 at 11:08 AM

Thanks - great article, clearly written and an excellent discussion with enhancements to follow :-)

Comment 17 by Matthew Fedak posted on 10/2/2012 at 5:23 AM

Thanks Ray, why did they not mention the expand in the jquery mobile docs I wonder. Second time now I been on your site during the jquery mobile project I'm making, keep up the good work.

Comment 18 by Raymond Camden posted on 10/2/2012 at 7:52 PM

Glad to help. To be fair to the docs, this feels like something that isn't necessarily appropriate there - more like a jQuery Mobile cookbook type thing.

Comment 19 by tony farrell posted on 10/11/2012 at 12:40 AM

this is nice - is there a way to make the two items into a seamless grouping?

thanks
tony

Comment 20 by Raymond Camden posted on 10/11/2012 at 1:13 AM

Sorry, I don't understand your question.

Comment 21 by Mike Backhouse posted on 4/11/2013 at 2:39 PM

Awsome work , have been googling this for ages works a treat

Comment 22 by Angus Marshall posted on 6/2/2014 at 7:09 PM

Works lovely as a test at my site http://www.homeducate.me/cg...

However, I'm trying to get it to work with:
jquery.mobile-1.4.1.min.css
jquery-1.10.2.min.js
jquery.mobile-1.4.1.min.js

and it fails (see http://www.homeducate.me/cg... ). Need those versions for other reasons :(

Any ideas?

Also, any ideas on how to display a "busy" twirly animation while the AJAX is doing its thing?

Any ideas?

Comment 23 by Raymond Camden posted on 6/2/2014 at 7:37 PM

You should learn to use your browser dev tools. :) As soon as I opened it I saw an error in the console: undefined is not a function

It is failing on the .live call, which was deprecated if i remember right, and should be replaced with .on.

Comment 24 by Angus Marshall posted on 6/3/2014 at 6:53 PM

Ah, thank you for the training :)
I tried the switch to .on but it still fails. I will tinker about with it and see what I can find. Thanks for the great code anyway and response to steer me in the right direction. It's much appreciated!

Comment 25 by ravi tiwari posted on 6/28/2014 at 6:16 AM

hi,
i am also facing the same issue.works good with old jquery but not with latest 1.4.2 jquery mobile and 1.11 jquery.can u please update ur guide for latest version of jquery

http://www.way2enjoy.com/to...

Comment 26 by Raymond Camden posted on 6/28/2014 at 6:30 AM

As I described above, you can replace .live with .on. See the docs on how it's API works.

Comment 27 by ravi tiwari posted on 6/28/2014 at 10:11 AM

Dear Ray,

thanks for your reply.i tried with all possible options including on but its not working. used js debugger then also no luck.request you, whenever you get some time, please try once in latest version of jquery for your this post.it will be great help

Comment 28 by Angus Marshall posted on 6/28/2014 at 11:48 AM

I also failed to get it working with .on
for later versions of JQuery/JQM and would very much appreciate an update. My coding skills are just not up to it, sorry :)

Comment 29 by Raymond Camden posted on 6/28/2014 at 8:50 PM

@ravi: Well, the first step is to see if you can listen to the expand/collapse events. So before even trying to load crap in via AJAX, ensure your event handlers work by checking the console to see if there are fired.

Using on, it should be

$("body").on("expand", "div")

Try that first. The docs for on may be found here: http://api.jquery.com/on/

I do not have plans on updating this post as - well - with ten years of posts I've got a lot of content that would need updating.

So I recommend following the steps I explained above - start simple and ensure you can listen to the event right.

Comment 30 by Raymond Camden posted on 6/28/2014 at 8:51 PM

Also, the official docs for jQM show examples: http://api.jquerymobile.com...

Comment 31 by james posted on 11/11/2014 at 3:55 PM

Anyone can update this post .please??????

its not working on latest version of Jquery