A reader pinged me yesterday with a simple problem that I thought would be good to share on the blog. He had a query of events that he wanted to use with jQuery UI's Accordion control. The Accordion control simply takes content and splits into various "panes" with one visible at a time. For his data, he wanted to split his content into panes designated by a unique month and year. Here is a quick demo of that in action.

I began by creating a query to store my data. I created a query with a date and title property and then add up to three "events" over the next twelve months. I specifically wanted to support 0 to ensure my demo handled noticing months without any data.

<!---
Create some fake data with dates
--->
<cfscript>
q = queryNew("date,title");
for(i=1; i<12; i++) {
	//for each month, we add 0-3 events (some months may not have data)
	toAdd = randRange(0, 3);
	
	for(k=0; k<toAdd; k++) {
		newDate = dateAdd("m", i, now());
		queryAddRow(q, {date:newDate, title:"Item #i##k#"});	
	}
}
</cfscript>

To handle creating the accordion, I had to follow the rules jQuery UI set up for the control. Basically - wrap the entire set of data in a div, and separate each "pane" with an h3 and inner div. To handle this, I have to know when a new unique month/year "block" starts. I store this in a variable, lastDateStr, and just check it in every iteration over the query. I also need to ensure that on the last row I close the div.

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>jQuery UI Accordion - Default functionality</title>
	<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
	<script src="//code.jquery.com/jquery-1.10.2.js"></script>
	<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
	<script>
	$(function() {
	$( "#accordion" ).accordion();
	});
	</script>
</head>
<body>
	<div id="accordion">

		<cfset lastDateStr = "">
		<cfloop query="q">
			<cfset thisDateStr = month(date) & "/" & year(date)>
			
			<!--- Is this datestr different? --->
			<cfif thisDateStr neq lastDateStr>
				<!--- We only 'close' a div if not on the first iteration --->
				<cfif currentRow neq 1>
					</div>
				</cfif>
				<cfoutput>
				<h3>#thisDateStr#</h3>
				</cfoutput>
				<div>
				<cfset lastDateStr = thisDateStr>
			</cfif>
			
			<cfoutput>
			#title#<br/>
			</cfoutput>
			
			<cfif currentRow is recordCount>
				</div>
			</cfif>
			
		</cfloop>
	</div>
</body>
</html>

And the end result:

So, not rocket science, but hopefully helpful to someone. Here is the entire template if you want to try it yourself.

<!---
Create some fake data with dates
--->
<cfscript>
q = queryNew("date,title");
for(i=1; i<12; i++) {
	//for each month, we add 0-3 events (some months may not have data)
	toAdd = randRange(0, 3);
	
	for(k=0; k<toAdd; k++) {
		newDate = dateAdd("m", i, now());
		queryAddRow(q, {date:newDate, title:"Item #i##k#"});	
	}
}
</cfscript>

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>jQuery UI Accordion - Default functionality</title>
	<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/smoothness/jquery-ui.css">
	<script src="//code.jquery.com/jquery-1.10.2.js"></script>
	<script src="//code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
	<script>
	$(function() {
	$( "#accordion" ).accordion();
	});
	</script>
</head>
<body>
	<div id="accordion">

		<cfset lastDateStr = "">
		<cfloop query="q">
			<cfset thisDateStr = month(date) & "/" & year(date)>
			
			<!--- Is this datestr different? --->
			<cfif thisDateStr neq lastDateStr>
				<!--- We only 'close' a div if not on the first iteration --->
				<cfif currentRow neq 1>
					</div>
				</cfif>
				<cfoutput>
				<h3>#thisDateStr#</h3>
				</cfoutput>
				<div>
				<cfset lastDateStr = thisDateStr>
			</cfif>
			
			<cfoutput>
			#title#<br/>
			</cfoutput>
			
			<cfif currentRow is recordCount>
				</div>
			</cfif>
			
		</cfloop>
	</div>
</body>
</html>