Earlier this week I decided to try an experiment. Unfortunately it was not a complete success. However, I feel like I got close enough to blog about it. I got to about 90% of where I wanted to be and maybe one of my readers can help me overcome the issues I ran into.
One of the more interesting features of HTML 5 is offline support. As you can guess, this is the ability to cache and store content for use when offline. On a desktop this isn't terribly useful. Most of us have continuous high speed connections. But on a mobile device it could be incredibly useful. I thought I'd create a simple demo based on the mobile hotel web site I built a few days ago. That site provided contact information for the hotel as well as a map and driving directions with GPS. If the user is offline, then the site won't work at all. I decided to aim for the following goals:
- At minimum, if I can view the mobile home page, contact page, and map, I could still get to the hotel if need be even if my mobile device is offline.
- At best, it would be cool if I could somehow know I was offline, I could hide the button that provides driving directions, then users who are offline won't be confused when they click the button and nothing happens.
I began by doing research at Dive Into HTML5 - one of the best, and simplest, sites for learning about HTML 5. They have a complete section just on offline support. It was pretty helpful and I definitely recommend reading it first as I'm not going to go over every detail. Ben Nadel also has a great post on the topic. Unfortunately, this feature in general can be very tricky. Some of my friends heard me cursing quite a bit this week. (In fact, I think I even said at one point that if this is the future of the web than we are all doomed!) Expect difficulty debugging and quite a bit of frustration finding firm answers and support for what follows.
The concept is simple enough. You create a file called a manifest. This manifest can define
- what is cached
- what is never cached
- and resource to use if you are offline and request something not cached
The manifest is a simple text file. To load the manifest, you can just add this to your HTML tag.
<html manifest="cache.manifest">
Taking this knowledge, I looked at my hotel web site. It consisted of a few pages and the resources jQuery Mobile itself uses. jQuery Mobile requires two JavaScript files, a CSS file, and a few images. Not much at all. So based on this I created the following manifest file:
#rev92 NETWORK:
*
CACHE:
contact.html
find.html
jquery.mobile-1.0a3.css
jquery-1.5.1.min.js
jquery.mobile-1.0a3.js
jquery.json-2.2.min.js
images/ajax-loader.png
images/form-check-off.png
images/form-check-on.png
images/form-radio-off.png
images/form-radio-on.png
images/icon-search-black.png
images/icons-18-black.png
images/icons-18-white.png
images/icons-36-black.png
images/icons-36-white.png
logo_apex.png
hotel.png
CACHE MANIFEST
The first line, CACHE MANIFEST, is required to make things work. The NETWORK line, and this is pretty confusing, tells your web page to allow it to load anything not found in the cache. This part is - frankly - weird. Because if you forget it, and you try to load something not in the cache, the resource will not load, even if you are online. That seems very counterintuitive and tripped me up for a while.
So - at this point - believe it or not - we are actually done. Kinda. If you monitor your Apache web logs you can actually see a request for your index page actually performing a HTTP request for all of the things list in the cache part of the manifest file. That's assuming you actually followed the directions on the Dive into HTML5 site and added the proper web server mime type for your manifest file. As a great example of how extremely frustrating this feature is, when I forgot to do this on my IIS server, things obviously broke. I made use of the JavaScript APIs for cache events, but get this. Apparently Firefox will notice an error with your cache manifest, but won't actually report what the error was! I spent about half an hour looking at the exception object because I was certain I was missing something obvious, but I wasn't ever able to determine what was wrong. Then on a whim I ran the app in Chrome. I had been using Firefox since it has a simple way to fake being offline. When I tried Chrome though I noticed it's Console automatically reported on cache events, including the error, and plainly stated that the mime type wasn't right.
Alright, so I should now have my "minimal" offline support wanted. But what about the offline/online support? Some browsers support a flag variable navigator.onLine. I decided to make a function then that would hide the Google Static map as well as the button when the user is offline. Here's what I used:
function drawOnline(){
if (navigator.onLine) {
$("#staticMap").show();
$("#drivingButton").show();
} else {
$("#staticMap").hide();
$("#drivingButton").hide();
}
}
I then added code to my document.start block in order to listen for online and offline events.
$(window).bind("offline online", function() {
drawOnline();
});
Finally, I added a call to drawOnline as well. My thinking here was that as soon as the page loaded I wanted to double check to see if the user was online. If the page was loaded from cache then I'd be able to hide the map and button right away. Here is a bigger part of my document.start block:
$(window).bind("offline online", function() {
drawOnline();
}); drawOnline();
$(document).ready(function() {
As a reminder, the complete code for the hotel site, the original version, can be found here.
So - that's it, right? Err, well, not quite. You can try this yourself here: http://coldfusionjedi.com/demos/drivingdemo/.
In Firefox, it worked. (Again, after spending a few hours tearing my hair out with the cache manifest file.) I could hit the site, click to view the map, and then go into offline mode. Right away the map and button disappeared. Then I switched to a mobile device. This is - after all - a mobile demo.
Results here were... disappointing. On both my Android device and my wife's iPhone, I created a shortcut to the device on the desktop. I loaded the site and clicked around a bit. I then went into Airplane mode. When I clicked the shortcut, on both devices I got a warning about being offline. Once past the warning though, it worked! I was able to click around. So that right there is pretty cool. If I was traveling and had data roaming off, I could still use the web app.
Unfortunately, the offline/online toggle didn't work at all. My buddy Todd Sharp discovered that this is currently broken in Android. Not much I can do about that. But in theory, it should have worked on the iPhone at least.
Sigh.... Some days I hate computers. Right before I posted this entry I double checked on the iPhone and Android devices. Both failed to load any sub pages. Home page loaded - but none of the links. No idea why. Clearing the cache and the offline storage on the Android did make it work again. The iPhone doesn't seem to have a way to clear the offline storage and I think that's the issue there.
I hope - despite the issues - that this is helpful for folks. I've included a complete zip of this version. If all my issues are the result of something small, and dumb, please call me out on it. I feel like I've got a "perfect" offline jQuery Mobile app so darn close and I'll be happy to do anything to get it working right!