Twitter: raymondcamden


Address: Lafayette, LA, USA

Building an offline capable mobile web site with jQuery Mobile

03-12-2011 66,802 views Mobile, jQuery, JavaScript, HTML5 31 Comments

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:

1) 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.

2) 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.

view plain print about
1<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:

view plain print about
1CACHE MANIFEST
2
3#rev92
4
5NETWORK:
6*
7CACHE:
8contact.html
9find.html
10jquery.mobile-1.0a3.css
11jquery-1.5.1.min.js
12jquery.mobile-1.0a3.js
13jquery.json-2.2.min.js
14images/ajax-loader.png
15images/form-check-off.png
16images/form-check-on.png
17images/form-radio-off.png
18images/form-radio-on.png
19images/icon-search-black.png
20images/icons-18-black.png
21images/icons-18-white.png
22images/icons-36-black.png
23images/icons-36-white.png
24logo_apex.png
25hotel.png

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:

view plain print about
1function drawOnline(){
2    if (navigator.onLine) {
3        $("#staticMap").show();
4        $("#drivingButton").show();
5    } else {
6        $("#staticMap").hide();
7        $("#drivingButton").hide();        
8    }
9}

I then added code to my document.start block in order to listen for online and offline events.

view plain print about
1$(window).bind("offline online", function() {
2    drawOnline();
3});

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:

view plain print about
1$(document).ready(function() {
2
3    $(window).bind("offline online", function() {
4        drawOnline();
5    });
6
7    drawOnline();

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!

Download attached file

31 Comments

  • Commented on 03-12-2011 at 5:48 PM
    Great Thanks for the Share Ray ... Especially the Link for 'Dive into HTML5' too ...

    :-)
  • Steve Judd #
    Commented on 03-14-2011 at 9:43 AM
    Ray,

    I had some issues, where for js and images in the cache manifest, I had to add path info from the root of the website. So for the cache manifest, it would be /demos/drivingdemo/jqueryblahblah

    Not sure if that would address your problem, but it worked for me with js, css, and images.
  • Jason Fisher #
    Commented on 03-15-2011 at 7:06 AM
    @Ray, this post made it into today's CodeProject newsletter down in the Developer News section. Good stuff!
  • Commented on 03-15-2011 at 7:07 AM
    @Jason: I hate to say it - but I have no idea what that is. Link me?

    @Steve: I plan on testing this later today.
  • Jason Fisher #
    Commented on 03-15-2011 at 7:26 AM
    Ha, sorry about that. Meant to include the link the first time. It's a daily newsletter of interesting tidbits from all sorts of different sources, both industry news and code development news.

    Here's the link for this morning's issue:
    http://www.codeproject.com/script/Mailouts/View.as...
  • Commented on 03-15-2011 at 7:28 AM
    Nice, I love the snarky comment:

    "Or you could figure out Xcode."

    That's going to be difficult on my PC. ;)
  • Commented on 03-15-2011 at 11:29 AM
    Just an FYI: http://www.theregister.co.uk/2011/03/15/apple_ios_...
  • Commented on 03-21-2011 at 6:17 AM
    I've uploaded /demos/drivingdemo2 and modified the cache manifest to use full relative paths as per Steve's suggestion. Anyone care to try it and see if it works better? (Not by my wife's iPhone.)
  • Anders Johnson #
    Commented on 05-27-2011 at 10:52 PM
    Hey Ray,
    Did you get a chance to try it out with the path info from the root of the website as per Steve's suggestion?

    I'm trying to build a jQuery Mobile web app for work, and it would be nice to know if caching with local storage is even possible using jQM, so that, if not, we can decide if it's still worthwhile to continue with the project.
  • Commented on 05-30-2011 at 6:50 AM
    To be fair, this is less a JQM issue and more a web browser issue in general. If it doesn't work, you can't blame JQM (afaik). That being said - no - I did not get a chance to test this again. I think I was hoping one of my readers would. :)
  • GEOFF #
    Commented on 08-24-2011 at 11:05 AM
    I've wrriten a jquery mobile app and was having problems when I cached it as the ajax calls to my web service stopped working
    however if I add the service.svc to the network section the ajax now works when cached.
  • Rodney #
    Commented on 11-20-2011 at 12:15 PM
    Just FYI, I just tried your mobile app on my Droid Incredible and it worked as you described. I browsed the site while connected, got driving directions, went to the home page, went to airplane mode, went back to the find us page and the map and get driving directions button were gone. Looks like it's working!
  • Rodney #
    Commented on 11-20-2011 at 12:17 PM
    Oops, forgot to also include in the above post that I turned off airplane mode again, then went back to the find us page and the map and get driving directions button were there again.
  • Commented on 11-20-2011 at 2:43 PM
    Glad it worked for you - I'm still a bit iffy on offline mode still. Need to play with it more.
  • Commented on 01-23-2012 at 4:26 PM
    Hello,
    Jus thought for future reference that the iPhone can remove web app data selectively or for all stored data, in settings > safari > developer. I've been working with HTML5 offline caching for about a year now and still find issues occasionally, however when it works efficiently, Im very impressed.
    Tom
  • Commented on 05-05-2012 at 11:16 AM
    Hey i am having problem viewing an application for a client of mine off line. This is what my manifest file looks like and what the mimi in my htaccess file looks like.

    CACHE MANIFEST

    #version 5


    CACHE
    index.html
    cresthollow.html
    http://jackandroseflorist.com/mobile/index.html
    http://jackandroseflorist.com/mobile/jackandroseco...
    http://jackandroseflorist.com/mobile/jackandroseco...#Gallery1" target="_blank">http://jackandroseflorist.com/mobile/jackandroseco...
    http://jackandroseflorist.com/mobile/cresthollow.h...
    assets/css/style4.css
    assets/portfolio/code.photoswipe.jquery-3.0.4.min.js
    assets/portfolio/lib/klass.min.js
    stylesheet" href="assets/portfolio/photoswipe.css
    assets/js/back.js
    assets/slider/jquery.flexslider-min.js
    assets/images/apple-touch-icon.png
    assets/slider/desires.jpg
    assets/slider/second.jpg
    assets/slider/third.jpg
    assets/slider/fourth.jpg
    assets/images/shadow1.png
    assets/images/headers/cresthollow.png

    NETWORK
    *
    http://*
    https://*


    htaccess file is in the root directory of the none mobile site should i create a new htaccess file and place it in the root directory of the mobile site on the server? maybe thats why it is not working right any advice or guidance would be great

    AddType text/cache-manifest .manifest
  • Commented on 05-07-2012 at 5:10 PM
    I'm afraid I can't quite understand your English here. You said htaccess - but your cache file doesn't go in a .htaccess file. It goes in a something dot manifest file. All htaccess does is the addtype. Maybe you meant that.

    And you then mentioned non-mobile versus mobile. Well, if you want the mobile version available offline, you need the cache manifest file there of course.
  • Commented on 03-03-2013 at 2:10 AM
    Raymond, did you ever get a web app to work properly offline? I spent countless hours on this a year back and then gave up thinking, someone else will notice that it's just not possible and in 1 year I'll revisit. So here I am! Is it possible in 2013 to run a multipage HTML5 web app offline on an iPhone - several days after being online?
  • Commented on 03-03-2013 at 8:49 AM
    I've not looked at this in a while. AppCache is just plain painful. I believe I can get it to work if I need to, but I haven't had a need to in a while. (I've mainly been doing PhoneGap for iOS.)
  • Shiva #
    Commented on 04-11-2013 at 11:04 PM
    Hi frds, Here u gave idea for Internet connection detect mobile automatically.. But i need code for How to Detect internet connection for System apps and also for mobile apps
  • Commented on 04-12-2013 at 6:29 AM
    I assume you Googled? First result:

    JavaScript: How to detect that the Internet connection is offline?
    http://stackoverflow.com/questions/189430/javascri...
  • Brett Ryan #
    Commented on 04-23-2013 at 12:51 AM
    You can clear iOS safari website data via Settings.app > Safari > Advanced > Website Data; click "Edit" and you can remove data for the relevant site.

    You can also clear cookies and data at Settings.app > Safari.
  • Rajiv #
    Commented on 06-08-2013 at 10:53 PM
    But how to cache the dynamic content that comes from the remote server and database.
  • Commented on 06-09-2013 at 10:29 AM
    Use one of the client-side storage options available to you: DOMStorage, IndexedDB, WebSQL perhaps.
  • Joel Anderson #
    Commented on 12-30-2013 at 2:44 PM
    Having just successfully completed a web app in Jquery Mobile that requires one component (page) to be available while offline, I can clarify a few things in this thread. First, it is NOT necessary to specify any images, css (stylesheets), or script (js) files in the cache manifest, as these things are automatically cached by the browser. Second, for the other items that you want to force to be cached, you MUST specify the full path, including, for resources from other web sites, the entire URL. Third, if your site will contain multiple pages that you want to be cached, then the cache manifest must be specified on the html tag in each of them. (FWIW, the mobile part of my testing has been done on an iPad and an iPod touch.)
  • Commented on 12-30-2013 at 3:26 PM
    Thanks for sharing this, Joel.
  • Joel Anderson #
    Commented on 12-31-2013 at 9:05 AM
    OK, sorry for another clarification so soon, but I ran into a few snags with further testing.

    My first point above, about not needing to put images, stylesheets, and scripts into the cache manifest, is apparently untrue.

    I encountered incomplete rendering and script processing when refreshing the page with a slimmed-down cache manifest file, although the main page's plain html came out just fine.

    I added all the lines back into my cache manifest, one at a time, and all the refresh problems went away only after I had added back everything I had thought I didn't need. Sadly, I even had to include the jquery and jqm files from the jquery cdn (adhering to my second point while I was at it).

    Even though this is an old post, I found it most helpful, and I hope my additions will save someone the agony that I suffered.
  • Commented on 12-31-2013 at 9:13 AM
    NP on the clarifications Joel, they will help others. AppCache is not the friendliest spec out there. :\
  • Commented on 01-06-2014 at 12:29 PM
    "...the proper web server mime type for your manifest file..."

    Why do I have to all the way across the internet to find my answers when I should be going to CC's Coffee House with my old friend Ray? ;-)

    I had this working with the iUI back in September of last year before switching to JQM for better JavaScript support. But I noticed my application was no longer caching for offline support. I was so upset until reading your article and remembering that I had upgraded my local installation of Tomcat and forgot to update my web.xml to support the manifest file.

    While reading your article, I found this webpage about multiple pages in the offline cache: http://www.studiosedition.com/articles/creating-mu... and I was able to confirm that using rel="external" in the A anchor element successfully accesses different pages in your cache.

    _Sigh._ I miss your talks, man. Thanks for your help, Jedi Master.
  • Commented on 01-06-2014 at 12:40 PM
    @Adam: Glad this was helpful - btw - feel free to ping me directly if you ever want to meet up and talk tech (or drink coffee ;). As it stands, I figure this blog post takes off a bit of debt I owe you from that Assembly class. ;)
  • Kshitiz Singh #
    Commented on 03-13-2014 at 4:35 AM
    http://www.bennadel.com/blog/2005-Experimenting-Wi...

    I have started work on saving offline data.
    found data

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty