How to make a collapsible split list in jQuery Mobile

Introduction:

Screen real estate is valuable, especially on a mobile device. If you have a lot of information you want to display that can be grouped under a common header / theme, a great solution is to use a collapsible listview found in the jQuery Mobile library.
 
However, what do you do when you want to combine the functionality of a collapsible list with that of something that has a common button among the list items that would allow you to edit / delete / perform some action on the list item that is common to all the items on the list (like a jQuery Mobile Split List)?
 
The following tutorial will walk you through on how to create a collapsible split list in jQuery Mobile and will utilize some custom CSS and JavaScript to get the job done.

Tutorial

Lets start off with a basic split list, taken and modified from the jQuery Mobile Listview tutorial (converted listview to split list, changes highlighted):

<ul data-role="listview" data-inset="true" data-split-icon="delete">
	<li>
		<a href="#">
			<h2>Stephen Weber</h2>
			<p><strong>You've been invited to a meeting at Filament Group in Boston, MA</strong></p>
			<p>Hey Stephen, if you're available at 10am tomorrow, we've got a meeting with the jQuery team.</p>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#">
			<h2>jQuery Team</h2>
			<p><strong>Boston Conference Planning</strong></p>
			<p>In preparation for the upcoming conference in Boston, we need to start gathering a list of sponsors and speakers.</p>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#">
			<h2>Avery Walker</h2>
			<p><strong>Re: Dinner Tonight</strong></p>
			<p>Sure, let's plan on meeting at Highland Kitchen at 8:00 tonight. Can't wait!</p>
		</a>
		<a href="#">Delete</a>
	</li>
</ul>

Using the code above, you should get something similar to the following:
(note the following is using jQuery Mobile 1.4.2 and jQuery 1.9.1, yours may look different if you’re using a different theme / version of jQuery Mobile / jQuery)
 

 
In this contrived example, that’s a decent amount of potentially useful information. However, on a mobile device, the amount that is displayed at one time is limited and if the user was looking for something at the bottom, they would have to scroll through the content of everything else. Lets add another div to enclose the content we want to hide:
(changes highlighted)

<ul data-role="listview" data-inset="true" data-split-icon="delete">
	<li>
		<a href="#">
			<h2>Stephen Weber</h2>
			<div style="display:none;">
				<p><strong>You've been invited to a meeting at Filament Group in Boston, MA</strong></p>
				<p>Hey Stephen, if you're available at 10am tomorrow, we've got a meeting with the jQuery team.</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#">
			<h2>jQuery Team</h2>
			<div style="display:none;">
				<p><strong>Boston Conference Planning</strong></p>
				<p>In preparation for the upcoming conference in Boston, we need to start gathering a list of sponsors and speakers.</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#">
			<h2>Avery Walker</h2>
			<div style="display:none;">
				<p><strong>Re: Dinner Tonight</strong></p>
				<p>Sure, let's plan on meeting at Highland Kitchen at 8:00 tonight. Can't wait!</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
</ul>

Resulting in:
 

 
The bulk of the content is gone, and the height changed from 307 px to 174 px, a difference of 133 px or ~44 px per list item. That’s a lot of recovered space…but how do we get the content back?
 
For that, we’ll need to write some custom javascript. What we need to do is target the link that was clicked, and un-hide the div that we had hidden. Luckily, this is very easy:

$(document).on("click","ul a", function(){
	$(this).children("div:last").toggle(200);
	if($(this).attr("title")) {
		console.log("clicked delete");
	} else {
		//Clicked the link to toggle the content
	}
});

Brief explanation:

$(document).on("click","ul a", function(){

This defines a function to be called whenever a link is clicked within an unordered list.

$(this).children("div:last").toggle(200);

$(this) refers to the link we’ve selected, .children(“div:last”) targets the last div element that is nested within the link (more on this in a minute), and .toggle(200) tells jQuery to hide/unhide the div, animating it over 200 ms.

if($(this).attr("title")) {

The text we put inside the link for the delete icon is used to populate the title attribute (in this case “Delete”). Since no title was supplied for the collapsible content, the title attribute will not be set, and the code within this if-statement will only be executed when the delete icon is clicked. If you provided a title attribute for the collapsible content, you could easy filter it out by doing a comparison in the if-statement.


When combined with the code above, we should get something that looks like this:
 

 
When you click on each of the list items, it’ll expand and collapse, displaying and hiding the content. Also, if you’re using Chrome, you can look in the browser console and change from “<top frame>” to “collapsible-split-list-3.html” and you’ll see “clicked delete” appear whenever you click the delete icon. Other browsers that allow the viewing of the console should also show “clicked delete”, but the method to find that is outside the scope of this tutorial.
 
Now, in the above javascript code, why did I specifically target the LAST div when there’s only 1 div to begin with? Because currently, there isn’t any indication that the list items can be clicked to display more information, and since jQuery Mobile doesn’t natively support collapsible split lists / multiple icons for lists, we need to add another div to put the icon in there:
The HTML (changes highlighted)

<ul data-role="listview" data-inset="true" data-split-icon="delete">
	<li>
		<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
			<h2>Stephen Weber</h2>
			<div style="display:none;">
				<p><strong>You've been invited to a meeting at Filament Group in Boston, MA</strong></p>
				<p>Hey Stephen, if you're available at 10am tomorrow, we've got a meeting with the jQuery team.</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
			<h2>jQuery Team</h2>
			<div style="display:none;">
				<p><strong>Boston Conference Planning</strong></p>
				<p>In preparation for the upcoming conference in Boston, we need to start gathering a list of sponsors and speakers.</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
	<li>
		<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
			<h2>Avery Walker</h2>
			<div style="display:none;">
				<p><strong>Re: Dinner Tonight</strong></p>
				<p>Sure, let's plan on meeting at Highland Kitchen at 8:00 tonight. Can't wait!</p>
			</div>
		</a>
		<a href="#">Delete</a>
	</li>
</ul>

The CSS for the “iconFloat” class:

.iconFloat {
	float: left;
	margin-top: 0px;
	margin-bottom: 0px;
	border-top-width: 0px;
	border-bottom-width: 0px;
	border-left-width: 0px;
	border-right-width: 0px;
	top: 2px; //this will vary depending on application
	padding-bottom: 0px;
}

Now we have a collapsible split list with a plus icon to indicate that there’s more content and a delete icon, but when you click on the list item, it expands but doesn’t turn into a minus icon like would logically be expected. To fix that, we need to add one more line to our javascript code above:
The Javascript (changes highlighted):

$(document).on("click","ul a", function(){
    $(this).children("div:last").toggle(200);
    if($(this).attr("title")) {
        console.log("clicked delete");
    } else {
        //Clicked the link to toggle the content
    }
	$(this).children("div:first").toggleClass("ui-icon-plus ui-icon-minus");
});

That is why we wanted to toggle the LAST div: because there would be 1 more div before it that would contain the collapsible icon (the FIRST div). The .toggleClass() function just adds / removes the specified classes (if the class is there, it removes it; if it isn’t, it adds it).
 
Combine it all together and you should have something like this:

Full HTML/CSS/Javascript

The full HTML/CSS/Javascript source used in the example above:

<html>
	<head>
		<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
		<style>
			.iconFloat {
				float: left;
				margin-top: 0px;
				margin-bottom: 0px;
				border-top-width: 0px;
				border-bottom-width: 0px;
				border-left-width: 0px;
				border-right-width: 0px;
				top: 2px;
				padding-bottom: 0px;
			}
		</style>
	</head>
	<body>
		<div data-role="page">
			<div role="main">
				<ul data-role="listview" data-inset="true" data-split-icon="delete">
					<li>
						<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
							<h2>Stephen Weber</h2>
							<div style="display:none;">
								<p><strong>You've been invited to a meeting at Filament Group in Boston, MA</strong></p>
								<p>Hey Stephen, if you're available at 10am tomorrow, we've got a meeting with the jQuery team.</p>
							</div>
						</a>
						<a href="#">Delete</a>
					</li>
					<li>
						<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
							<h2>jQuery Team</h2>
							<div style="display:none;">
								<p><strong>Boston Conference Planning</strong></p>
								<p>In preparation for the upcoming conference in Boston, we need to start gathering a list of sponsors and speakers.</p>
							</div>
						</a>
						<a href="#">Delete</a>
					</li>
					<li>
						<a href="#"><div class="iconFloat ui-btn ui-corner-all ui-icon-plus ui-btn-icon-notext"></div>
							<h2>Avery Walker</h2>
							<div style="display:none;">
								<p><strong>Re: Dinner Tonight</strong></p>
								<p>Sure, let's plan on meeting at Highland Kitchen at 8:00 tonight. Can't wait!</p>
							</div>
						</a>
						<a href="#">Delete</a>
					</li>
				</ul>
			</div>
		</div>
		
		<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
		<script src="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script>
		<script lang="text/javascript">
		$(document).on("click","ul a", function(){
			$(this).children("div:last").toggle(200);
			if($(this).attr("title")) {
				console.log("clicked delete");
			} else {
				//Clicked the link to toggle the content
			}
			$(this).children("div:first").toggleClass("ui-icon-plus ui-icon-minus");
		});
		</script>
	</body>
</html>

Conclusion

By including 2 extra div’s per list item, a little custom CSS, and some minor event handling Javascript, we can effectively create a functional collapsible split list. From here, you can easily add function calls to the part of the code for when the user clicks the delete button or choose to do an additional task when the user expands/collapses the content.
 
 
Let me know if you found this tutorial helpful and if you’re using collapsible split lists in the comments below.
 

Automatically updating version control!

There is no logical reason why I am THIS excited about getting my post-commit hook working. But I am! Now when I commit changes from my working copy, the post-commit hook will automatically update the working copy in the folder serving the “beta” sub-domain.
 
In order to get the post-commit hook working with svn, I had to create a small C program (as recommended by the official documentation). I copied it (modifying the paths), compiled it via SSH on my web host and tested it through SSH and found it to work successfully. However, when I made a commit, the post-commit hook failed with exit code 255 with no output.
 
I double-checked my permissions on the C program and tried again, but still the same error. I then tried commenting out the call to the C program and added a simple “whoami >> [path]/whoami.txt” to see who was executing the post-commit hook.
 
Post-commit hook failed with exit code (255) and no output.
 
Ok, NOW we’re getting somewhere. Whatever user account that’s trying to access the post-commit hook script can’t even access the script. I tried chmod-ing the repository, recursively, to 777 (read/write/execute for all) and did another commit. SUCCESS! I found a “whoami.txt” file, but surprisingly, it was my login (which had permissions to begin with).
 
Un-commenting the line that executes the C program to update a different working copy, I made another commit. This time, I started getting meaningful failure messages. The first message stated an authentication failure, so I modified the C program to include my username and password and recompiled it. Another error but this time it was due to requesting interaction (“are you sure you want to overwrite…”). Modified the C program one last time, adding the no-interaction flag, compiled and tested one last commit.
 
Bingo!
 
No error messages and the beta section of the website was updated automatically!
 
In short, to get the post-commit hooks working with svn I needed to:
 

  1. (via SSH) CHMOD -R 777 [path to repo]
  2. Create the following C program:
    #include <stddef.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(void) {
       execl("[path to svn executable]", "svn", "update", "[path to WC to update]", "--non-interactive", "--username", "[username]", "--password", "[password]",(const char *) NULL);
       return(EXIT_FAILURE);
    }

    Note: MUST have blank line at the end (at least my gcc compiler complained about the lack of one the first time). The code formatter above removes it.

  3. Upload the C-source to my website and compile
  4. (via SSH) CHMOD +s [compiled C-program]
  5. Since my host is a linux host, create a file in the “hooks” directory of the repo named “post-commit”
  6. Add the following two lines to the “post-commit” file:
    #!/bin/sh
    [path to repo]/hooks/[compiled C-program]
  7. Test the post-commit hook by performing a commit

Fluid Square Calendar with jQueryUI Datepicker Tutorial

In this tutorial I’ll show you the steps taken to customize an un-styled jQueryUI Datepicker widget as well as how to overcome a few common formatting problems relating to styling the jQueryUI Datepicker, using pure CSS. It will take you from starting with a completely blank document to having a fully structured HTML page using the jQuery Mobile framework with a fluid, square calendar.
 
Disclaimer: The following was only tested using the latest version of Google Chrome.

Getting Started

First thing you need is the stand-alone Datepicker Widget and its CSS. You can use those download links or you can customize your jQueryUI package yourself by going to jQueryUI’s Download Builder.
 
Then you’ll need to create the page that will hold the datepicker. You can do this manually, or you can copy the code below for a jQuery Mobile version. I’ve highlighted the important pieces of the code.
 

<!doctype html>

<html lang="en">
    <head>
        <meta charset="utf-8">

        <title>Calendar Test</title>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <meta name="description" content="jQuery Calendar Test">
        <meta name="author" content="B-rad">
        <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.css" />
		<link rel="stylesheet" href="jquery-ui-1.10.3.css" />
		<style>
		
		</style>
    </head>
    <body>
		
        <div data-role="page" id="dailyMinder">
            <div data-role="header" data-position="fixed" data-tap-toggle="false">
                <h1>Calendar Test</h1>
            </div>
			
            <div data-role="main" class="ui-content">
				<div id="calendar"></div>
            </div>
        </div>
		
        <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
        <script src="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.js"></script>
		<script src="jquery-ui-datepicker.min.js"></script>
		<script type="text/javascript">
		$('#calendar').datepicker({
			inline: true,
			firstDay: 0,
			showOtherMonths: true,
			dayNamesMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
		});
		
		</script>
    </body>
</html>

The first highlights add the jQuery Mobile stylesheet from their CDN (using a schemeless URI) and add the custom, no-theme stylesheet for the Datepicker (your path may differ depending on where you extracted it to).
 
The second highlight is where the Datepicker will appear.
 
The last highlight adds the jQuery and jQueryMobile javascript (from their CDN), the jQueryUI-datepicker javascript, and the javascript necessary to display a Datepicker whose first day of the week is Sunday, will display the other months’ days and specifies the shorthand notation for the days of the week.
 
You should now have a page that looks like the following:

The CSS – Basic Customization

Now that you have all the necessary files and setup work done, lets start by adding borders to the calendar. Find the “.ui-datepicker td” class and change from “border: 0;” to the following:

.ui-datepicker td {
	border: 1px solid #428bdd; /* edited - border: 0; */
	padding: 1px;
}

While we’re at it, lets make the title bold, a little larger, make it so the cursor doesn’t change while mousing over the title, and add the “Prev/Next” words in there to change the months:

.ui-datepicker .ui-datepicker-title {
	margin: 0 2.3em;
	line-height: 1.8em;
	text-align: center;
	font-weight: bold;	/* make month title bold */
	font-size: 1.2em;	/* make month title larger */
	cursor: default;	/* keep default cursor over month */
}
.ui-icon {
	display: block;
	/*text-indent: -99999px;	remove this line */
	overflow: hidden;
	background-repeat: no-repeat;
}

We can see that the “Prev/Next” don’t quite line up with the rest of the Datepicker and the cursor isn’t the appropriate type, so lets fix that:

.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
	cursor: pointer; /* link cursor */
	top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
	top: 4px;		/* lower the text */
	left: 2px;
}
.ui-datepicker .ui-datepicker-next {
	top: 4px;		/* lower the text */
	right: 10px;	/* edited - right: 2px; */
}
.ui-datepicker .ui-datepicker-next-hover {
	right: 9px;		/* edited - right: 1px; */
}

Lastly, lets make it span the whole width of the page (fluidly):

.ui-datepicker {
	/*width: 17em;	remove this line */
	padding: .2em .2em 0;
	display: none;
}

You should now have something that looks like:

The CSS – Fixing The Height

If you’ve ever tried to customize the height of a table row based on the width, you’ve probably encountered a LOT of problems. This section will show you how to change the table row / cell height based on the width, and also how to fix the last column of the table not having a fixed width or the last table column not having the same width.
 
To get the height of the cells changed, add the following pseudo-class after “.ui-datepicker td”

.ui-datepicker td:before {
	content: "";
	display: block;
	padding-top: 100%;	/* set height of cell (padding based on width) */
}

Getting closer, but the columns aren’t the same size and the dates aren’t identically placed! Since the dates are nested inside links, we change the styles for the links, like so:

.ui-datepicker td a {
	display: block;
	padding: .2em;
	text-align: center;			/* center the date, optional */
	text-decoration: none;
	position:  absolute;		/* position the date in the cell */
	top: 0;						/* put the link at the top */
	left: 0;					/* put the link at the left */
	bottom: 0;					/* extend the link to the bottom */
	right: 0;					/* extend the link to the right */
}

Now the dates have disappeared! To get them to come back we need to change the layout of the table cell:

.ui-datepicker td {
	position: relative;	/* position elements relatively */
	border: 1px solid #428bdd; /* edited - border: 0; */
	padding: 1px;
}

Now the dates have returned and the entire cell is clickable. Now for the magical part: making the height of table cells the same as the width. To do that we just as 1 more line to our “.ui-datepicker table” class:

.ui-datepicker table {
	width: 100%;
	table-layout:fixed !important; /* sets width of each column equally (and in turn, their height) */
	font-size: .9em;
	border-collapse: collapse;
	margin: 0 0 .4em;
}

And that’s it!

You should now have a full width, fluid calendar / datepicker with square cells like so:

If you want to change the height of the cells, change the padding in the pseudo-class we added earlier. For example, having the cells half as tall as they are wide is accomplished with:

.ui-datepicker td:before {
	content: "";
	display: block;
	padding-top: 50%;	/* height half the width */
}