About a week or so ago I whipped up a simple demo that showed using swipe-based navigation within jQuery Mobile (Using swipe gestures for navigation in jQuery Mobile). The idea was simple - look at how jQuery Mobile could listen for swipe events and then use them to navigate to the next page instead of using the traditional button click. I don't know about you, but it's amazing how quickly the swipes seem to be becoming a standard way to work with an application. A few weeks back I found myself about to reach out to my desktop monitor to do a swipe against the screen. My daughter, all of 9, already learned that swipes work on an iPhone, and when I handed her my Nook, she just knew a swipe would work in a book. So I think there's a good reason to work with this type of user interaction. Today's blog entry is another example of this - an Art Browser.
What follows is the complete code for my application. It's a one page application that handles getting art work from the database and displaying one image at a time. Swipe events can be used to go to the next piece of art, or return to the previous entry. Let's look at the code.
<!DOCTYPE html>
<html>
<head>
<title>Art Browser</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>
<script>
$(document).ready(function() { $('.artbrowserPage').live('swipeleft swiperight',function(event){
if (event.type == "swiperight") {
var prev = $("#previndex",$.mobile.activePage);
var previndex = $(prev).data("index");
if(previndex != '') {
$.mobile.changePage({url:"index.cfm",type:"get",data:"index="+previndex},"slide",true);
}
}
if (event.type == "swipeleft") {
var next = $("#nextindex",$.mobile.activePage);
var nextindex = $(next).data("index");
if(nextindex != '') {
$.mobile.changePage({url:"index.cfm",type:"get",data:"index="+nextindex});
}
}
event.preventDefault();
});
});
</script>
</head>
<body> <cfoutput>
<div data-role="page" data-theme="e" class="artbrowserPage"> <div data-role="header" data-backbtn="false">
<h1>#artdata[url.index].name#</h1>
</div> <div data-role="content">
<p>
<img src="artgallery/#artdata[url.index].image#" title="#artdata[url.index].name#"><br/>
<br/>
Item #url.index# out of #arrayLen(artdata)#<br/>
Swipe left and right to browse.
</p>
</div> <span id="previndex" data-index="#previndex#"></span>
<span id="nextindex" data-index="#nextindex#"></span> </div>
</cfoutput> </body>
</html>
<!--- load up our index of art if we don't have it already --->
<cfset artdata = cacheGet("artdata")>
<cfif isNull(artdata)>
<cfquery name="getart">
select artname, largeimage
from art
</cfquery>
<!--- quickly filter to art we have a picture for, in a real app this wouldn't be an issue --->
<cfset artdata = []>
<cfloop query="getart">
<cfif fileExists(expandPath("./artgallery/#largeimage#"))>
<cfset arrayAppend(artdata, {name=artname, image=largeimage})>
</cfif>
</cfloop>
<cfset cachePut("artdata", artdata, createTimeSpan(0,0,1,0))>
</cfif>
<cfparam name="url.index" default="1">
<cfif not isNumeric(url.index) or url.index lte 0 or round(url.index) neq url.index or url.index gt arrayLen(artdata)>
<cfset url.index = 1>
</cfif>
<cfset previndex = nextindex = "">
<cfif url.index gte 2>
<cfset previndex = url.index-1>
</cfif>
<cfif url.index lt arrayLen(artdata)>
<cfset nextindex = url.index+1>
</cfif>
Before we dive in - you should definitely read the previous entry to get up to speed with the basics. The first 16 lines or so represent my business logic. Normally this would not be in the page at all. For simplicity sake though I've got it included here. If you don't know ColdFusion (and I expect I'm getting visitors to the site lately who don't), the basic gist is - "Check the cache for an array of art data, and if it doesn't exist, query the database and check the file system for available images to store into my cache." At the end of this block we've got an array of art names and image urls.
The next block is also ColdFusion specific. It simply looks for a URL parameter to indicate which piece of art we are viewing. I do a basic bit of validation (we all validate our URL parameters, right?) and then create variables to represent the previous and next index. Note they will be blank at the beginning or end of the art list.
Ok, now for the JavaScript. I begin with a jQuery selector based on the class of my page. Again, if you've read my previous entry, you will remember that I need my event listener to pick up new 'pages' as they come into the DOM. Since I have the same page being loaded with different data inside, the class based listener is going to pick up on the changes. (I really think that this particular method is something I'm going to change later on. It just doesn't feel... 100% solid. Please keep that in mind. jQuery Mobile is new to me as well)
Inside this event handler is code very similar to my previous example. I'm storing my data a bit differently (as we will see later in the code) so I have to fetch it differently, but the concept is the same. Based on the direction I'm swiping, I need to possibly change pages. Pay particular attention to the swiperight section. For that event, I want to reverse my transition. So I have to specify the second attribute ("slide") as well as the 3rd "false" to signify the reversed version of the normal slide transition. This took me a little bit to figure out. The short version is - this is how I reverse the page transition. The example in the next IF block uses all the defaults.
So - the HTML is rather simple. I output a dynamic record from my cache. I then use two spans to store data values for my index. These will either be a number or a blank string, and my jQuery code will handle them accordingly.
You can play with this here:
It works - and works well I think - but I'd like to change two things:
- The art image is a bit small on the device. Not unsuable of course, but I'd like to make the image bigger.
- Todd Sharp suggested preloading the images. I haven't done image preloaders since the last time I wrote a simple rollover, but I think that would be a good idea. If we expect a user to possibly sit there and stare at the art for a few seconds, we should have enough time to fetch the next image. Since jQuery Mobile loads the next page via Ajax, we could actually fire off an immediate process to start fetching all the images in order. That's it - I've decided - I'm going to do a follow up and try that.
That's all I've got. Any comments on the implementation?