Creating mobile Web applications with HTML 5, Part 3: Make mobile Web applications work offline with HTML 5

Enable your application to function with or without an Internet connection

Part of the appeal of mobile applications is that you can take your application and its data with you wherever you go. One reality of mobile is, at times, a mobile device does not have a working connection to the Internet. This might seem to be an insurmountable problem for mobile Web applications. However, Web applications have evolved and become capable of working offline. In this article, you will learn how to offline-enable your mobile Web application and learn to detect when your application goes from offline to online and vice versa.

Share:

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin is an architect at eBay. He is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon, and AjaxWorld. To get a preview of what he is working on next, follow @michaelg on Twitter.



29 June 2010 (First published 02 June 2010)

Also available in Chinese Russian Japanese Vietnamese

08 Jun 2010: Added links to Part 4 of this series in About this series, Summary, and Resources sections.

29 Jun 2010: Added links to Part 5 of this series in About this series, Summary, and Resources sections.

About this series

HTML 5 is a very hyped technology, but with good reason. It promises to be a technological tipping point to bring desktop application capabilities to the browser. As promising as it is for traditional browsers, it has even more potential for mobile browsers. Even better, the most popular mobile browsers have already adopted and implemented many significant parts of the HTML 5 specification. In this five-part series, you will take a closer look at several of those new technologies that are part of HTML 5 and can have a huge impact on mobile Web application development. In each part, you will develop a working mobile Web application showcasing an HTML 5 feature that you can use on modern mobile Web browsers like the ones found on the iPhone and Android-based devices.


Prerequisites

In this article, you will develop Web applications using the latest Web technologies. Most of the code here is just HTML, JavaScript, and CSS—the core technologies of any Web developer. The most important thing you will need are browsers to test things on. Most of the code in this article will run on the latest desktop browsers with some noted exceptions. Of course you must test on mobile browsers too, and you will want the latest iPhone and Android SDKs for those. In this article, iPhone SDK 3.1.3 and Android SDK 2.1 were used. See Resources for links.


Why make your application work offline?

Frequently used acronyms

  • Ajax: Asynchronous JavaScript + XML
  • API: Application Programming Interface
  • CSS: Cascading stylesheet
  • HTML: Hypertext Markup Language
  • HTTP: Hypertext Transfer Protocol
  • JSON: JavaScript Object Notation
  • JSONP: JSON with padding
  • MIME: Multipurpose Internet Mail Extensions
  • PDF: Portable Document Format
  • SDK: Software Developer Kit
  • UI: User Interface
  • URL: Uniform Resource Locator
  • W3C: World Wide Web Consortium
  • WAP: Wireless Application Protocol
  • XML: Extensible Markup Language

Offline Web applications are attractive to both users and developers for a number of reasons. Many developers would love to be able to write a single Web application that works on all of the most popular smartphones instead of writing native applications for each platform. Just because that would be convenient to developers does not mean that it would be desired by users. For that to happen, mobile Web applications have to be able to provide many (or most) of the same features that native mobile applications can provide. Working offline is definitely one of those features. Some applications will rely heavily on data and services from the Internet—whether or not they are a mobile Web or native application. For these applications, it is understood that functionality might be reduced if the user does not have a good connection to the Internet. However, the application cannot fail completely just because the user's connection is not good. With a traditional Web application that is exactly what would happen.

Offline capabilities bring mobile Web applications closer to parity with native applications. They have some other benefits too. Web browsers have always cached static resources. They rely on metadata from the HTTP response headers sent by your Web servers to retrieve the HTML, JavaScript, CSS, and images needed to render the page. If everything needed to render the page is cached, then the page loads very quickly. However, if something is not cached, then it can slow down everything dramatically. This happens more often than you might like. Maybe one CSS file had a different Cache-Control header than all of the other files, or maybe the browser kicked out of cache because it was running out of allocated space.

With offline applications, you are assured that everything is cached. Browsers will always load everything from cache, though you also get control over what should not be served from cache. It is a common Ajax hack to add an extra timestamp parameter to Ajax GET requests (or even worse to use POST when GET is appropriate) to avoid browsers caching a response. You won't need this hack for offline-enabled Web applications.

Offline applications sound great, so it must be complex to create one, right? Actually, it is quite simple. You need to do three things:

  1. Create an online manifest file
  2. Tell the browser about the manifest file
  3. Set the MIME type on the server

The offline manifest

One key file is involved: the cache manifest for your application. This file tells the browser exactly what to cache (and, optionally, what not to cache.) This becomes the source of truth for your application. Listing 1 shows an example of a simple cache manifest.

Listing 1. Simple cache manifest
CACHE MANIFEST
# Version 0.1
offline.html
/iui/iui.js
/iui/iui.css
/iui/loading.gif
/iui/backButton.png
/iui/blueButton.png
/iui/cancel.png
/iui/grayButton.png
/iui/listArrow.png
/iui/listArrowSel.png
/iui/listGroup.png
/iui/pinstripes.png
/iui/redButton.png
/iui/selection.png
/iui/thumb.png
/iui/toggle.png
/iui/toggleOn.png
/iui/toolbar.png
/iui/whiteButton.png
/images/gymnastics.jpg
/images/soccer.png
/images/gym.jpg
/images/soccer.jpg

This file lists all of the files that your application needs to function properly. This includes HTML files as well as JavaScript, CSS, and images. It could include videos, PDFs, XML files, and so on. Notice that all of the URLs in this example are relative. Any relative URLs must be relative to the cache manifest file. In this case, the cache manifest file is in the root directory of your Web application. Compare the directory structure in Listing 2 to the relative URLs in Listing 1.

Listing 2. Text version of the directory structure for the Web application
  Name
V images
    gymnastics.jpg
    soccer.png
V iui
    backButton.png
    blueButton.png
    cancel.png
    grayButton.png
    iui.css-logo-touch-icon.png
    iui.css
    iui.js
    iuix.css
    iuix.js
    listArrow.png
    listArrowSel.png
    listGroup.png
    loading.gif
    pinstripes.png
    redButton.png
    selection.png
    thumb.png
    toggle.png
    toggleOn.png
    toolbar.png
    toolButton.png
    whiteButton.png
  manifest.mf
  offline.html
> WEB-INF

You might have noticed that this application is using the iUI framework. This is a popular JavaScript+CSS kit for giving mobile Web applications the look and feel of native iPhone applications. As you can see from Listing 1 and Listing 2, the framework uses several images to go along with its JavaScript and CSS files. However, all of these files will be cached by the browser and will be usable in offline mode as long as they are listed in the manifest.

Another critical thing to notice in Listing 1 is the version info. This is not part of the specification. In fact, it is just a comment in the file. However, it is key to have something like this that you can use to tell the browser that there is a new version of your application. Suppose you changed some HTML or some JavaScript or even just an image. If you do not change the manifest, the browser will never bother to load the new version of the resource that you modified. There is no expiry on a cache manifest, so everything stays cached unless the user clears the cache or the manifest file changes. The browser will check to see if there is a new manifest file. To indicate a new manifest file, you simply need to change something/anything in the existing manifest file. Going back to your example of changing the HTML on the page, if you changed the HTML and changed the version string in the manifest file, then the browser will know that the resources have changed and to download them again. Putting a version number in a comment is an easy way to manage this lifecycle.


Tell the browser about the manifest

There is one more missing piece to enabling offline caching of your Web application. The Web browser needs to know that you want to enable caching and where to find your cache manifest file. Listing 3 shows this very easy way.

Listing 3. Offline enabled Web page
<!DOCTYPE html>
<html>
<html manifest="manifest.mf">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta name="viewport" content="width=device-width; initial-scale=1.0; 
        maximum-scale=1.0; user-scalable=0;"/>
    <meta name="apple-touch-fullscreen" content="YES" />
    <link rel="apple-touch-icon" href="/iui/iui-logo-touch-icon.png" />
    <style type="text/css" media="screen">@import "/iui/iui.css";</style>
    <script type="application/x-javascript" src="/iui/iui.js"></script>
    <title>Let's do it offline</title>
</head>
<body>
    <div class="toolbar">
        <h1 id="pageTitle">Going offline</h1>
        <a id="backButton" class="button" href="#"></a>
    </div>
    <ul id="menu" title="Sports" selected="true">
        <li><a href="#gym"><img height="80" width="80" 
src="/images/gym.jpg" align="middle"/>
            <span style="display:inline-block;
 vertical-align:middle">Gymnastics</span></a></li>
        <li><a href="#soccer"><img src="/images/soccer.jpg" 
align="middle"/>
        <span style="display:inline-block; 
vertical-align:middle">Soccer</span></a></li>
    </ul>
    <div id="gym" title="Gymnastics" class="panel">
        <img src="/images/gymnastics.jpg" alt="Boys Gymnastics"/>
    </div> 
    <div id="soccer" title="Soccer" class="panel">
        <img src="/images/soccer.png" alt="Boys Soccer"/>
    </div>
</body>
</html>

The most important aspect of this HTML is the root html element. Notice that it has an attribute called manifest. This what tells the browser that this Web page can function offline. The value of the manifest parameter is the URL to the Web page's cache manifest file. Again, this can be a full URL, though in this case it is a relative (to the given Web page) URL. The other thing to note here is the DOCTYPE for the page. This is the canonical doctype for HTML 5 Web pages. The offline Web application specification does not dictate that you use this DOCTYPE; however, it is recommended that you do. Otherwise, some browsers might not recognize the page as an HTML 5 page and might ignore the cache manifest. The rest of the HTML is just a simple example of using iUI. Figure 1 shows what this page looks like on the iPhone simulator.

Figure 1. Offline Web application running on iPhone simulator
Screen capture of offline Web app running on iPhone simulator for Sports app with Gymnastics and Soccoer options

Testing an offline application can be a little tricky. If you can, the easiest way to do it is to deploy your application to a Web server. Then you can access the page once, turn off your Internet connection, and try to access it again. If anything fails, then you might have left out some files in the cache manifest. Before you give this a try, you will need to do one critical piece of configuration to your Web server.


Web server configuration

Listing 3 showed that you indicate the location of your cache manifest by using the manifest attribute on the root html element of your Web page. However, the specification for cache manifests dictates that the browser must do an additional validation step when it downloads and processes a cache manifest. It must check the MIME type of the cache manifest, and that type must be text/cache-manifest. This usually means that you need to configure your Web server to set this MIME type for a static file, or you must write some code to dynamically create the file and set the MIME type. The former is definitely the more efficient way to do things, but sometimes you need to do the latter if you do not have control over the server configuration (such as a server in a shared or hosted environment). If you control the server and if you are using a Java™ application server, you can configure this as part of the web.xml file of Web application. Listing 4 shows an example of this.

Listing 4. Configuring web.xml to set MIME types
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<!-- Servlets go here -->
    <mime-mapping>
        <extension>mf</extension>
        <mime-type>text/cache-manifest</mime-type>
    </mime-mapping>    
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

The key section here is obviously the mime-mapping element. In this case you are saying that for any files that end with the .mf extension, give them a MIME type of text/cache-manifest. Of course, it is even more efficient to serve such files from a server that is dedicated to serving static content such as an Apache Web server. In a typical Apache installation, you will just need to modify the mime.types file in the httpd/conf directory as in Listing 5.

Listing 5. Setting MIME type in mime.types
# This file controls what Internet media types are sent to the client for
# given file extension(s).  Sending the correct media type to the client
# is important so they know how to handle the content of the file.
# Extra types can either be added here or by using an AddType directive
# in your config files. For more information about Internet media types,
# please read RFC 2045, 2046, 2047, 2048, and 2077.  The Internet media type
# registry is at <http://www.iana.org/assignments/media-types/>.

# MIME type                     Extensions
text/cache-manifest             mf
# many more mappings...

In both examples, you use mf for the extension of your manifest file, since that file was manifest.mf. This is entirely arbitrary. You can make it .manifest or .foo, as long as the extension of the manifest file matches the extension used in the mapping in your configuration file. Note that other application and Web servers can have different configuration mechanisms. Now that you have seen the essential ingredients in creating offline mobile Web applications using HTML 5, look at a more complicated example to explore more of the capabilities of offline mobile Web applications.


Advanced example

In the previous example, all of the content was static. It was nice to be able to see everything in offline mode. A more typical application needs to read dynamic data from its server and Web services. To make your example more realistic, pull in some data from Twitter. If you have read the previous articles in this series, then this will be familiar to you (see Resources). To start, look at your modified HTML for this example in Listing 6.

Listing 6. Modified HTML
<body onload="init()">
    <div class="toolbar">
        <h1 id="pageTitle">Going offline</h1>
        <a id="backButton" class="button" href="#"></a>
    </div>
    <ul id="menu" title="Sports" selected="true">
        <li><a href="#gym">
            <img height="80" width="80" src="/images/gym.jpg" align="middle"/>
            <span style="display:inline-block; vertical-align:middle">Gymnastics</span>
        </a></li>
        <li><a href="#soccer"><img src="/images/soccer.jpg" align="middle"/>
            <span style="display:inline-block; vertical-align:middle">Soccer</span>
        </a></li>
        <li id="online" style="display: none"><img src="/images/online.jpg"/></li>
    </ul>
    <ul id="gym" title="Gymnastics"></ul> 
    <ul id="soccer" title="Soccer"></ul>
</body>

The main difference is that the gym and soccer elements are now lists, and are empty. You will fill them with tweets from Twitter on gymnastics and soccer, respectively. Notice also a list item element with id online that shows an image that will be used to indicate to the user whether the application is online or offline. However, this element is hidden by default, or, the default mode is offline. The body element specifies an init function is to be called once the body loads. Listing 7 shows this function.

Listing 7. Page initialization JavaScript
function init(){
    if (navigator.onLine){
        searchTwitter("gymnastics", "showGymTweets");
        searchTwitter("soccer", "showSoccerTweets");
        $("online").style.display = "inline";
    } 
    gymTweets = localStorage.getItem("gymnastics");
    if (gymTweets){
        gymTweets = JSON.parse(gymTweets);
        showGymTweets();
    }
    soccerTweets = localStorage.getItem("soccer");
    if (soccerTweets){
        soccerTweets = JSON.parse(soccerTweets);
        showSoccerTweets();
    }
    document.body.addEventListener("online", function() {
            $("online").style.display= "inline";
            applicationCache.update();
            applicationCache.addEventListener("updateready", function() {
                applicationCache.swapCache();
            }, false);
        }, false);
    document.body.addEventListener("offline", function() {
        $("online").style.display = "none";
    }, false);        
}

The first thing that this code does is check to see if you are online or offline. If you are online, then it displays the online image. Even more importantly, if you are online then data is loaded from Twitter by calling the searchTwitter function. Again, this is a technique (explained in previous articles in this series—see Resources) that lets you search Twitter directly from the browser using JSONP. Next, you attempt to load existing tweets from localStorage. If you are familiar with localStorage, this is another HTML 5 capability that works quite well with offline mode. Check out Part 2 in this series to learn more about it (see Resources). Notice for both new searches (kicked off if you detect that you are online) and for the loading of locally saved tweets, the showGymTweets and showSoccerTweets functions are invoked. These are similar functions, and Listing 8 shows showGymTweets.

Listing 8. Show Gym tweets
function showGymTweets(response){
    var gymList = $("gym");
    gymList.innerHTML = "";
    if (gymTweets){
        if (response){
            gymTweets = response.results.reverse().concat(gymTweets);
        }
    } else {
        gymTweets = response.results.reverse();
    }
    showTweets(gymTweets, gymList);
    localStorage.setItem("gymnastics", JSON.stringify(gymTweets));
}

This function can display locally saved tweets, new tweets from Twitter, or a combination of both, if both exist. Most importantly, it saves everything locally, building your local data cache of tweets. This is all typical code for managing both locally cached data, and live data from the servers. It allows for the application to operate smoothly whether it is online or offline.

Going back to Listing 7, the last thing you do is register event handlers. You are registering for online and offline events. This will tell you when the online or offline status of the browser changes. At the very least you can change the online image, toggling if it is displayed or not. In the case that the application comes online after being offline, you access the applicationCache object. This is an object representing all of the resources being cached as declared in the cache manifest. In this case, you call its update method. This method tells the browser to check whether it detects an update to the applicationCache. As mentioned earlier, the browser first checks for an update to the cache manifest file. You add another event listener to check for an update to the cache available. If so, then you call the swapCache method on applicationCache. This will re-load all of the files specified in the cache manifest file.

Speaking of the cache manifest file, you need one final touch for this advanced example. The cache manifest file needs to be modified as in Listing 9.

Listing 9. Modified cache manifest
CACHE MANIFEST
# Version 0.2
CACHE:
offline.html
json2.js
/iui/iui.js
/iui/iui.css
/iui/loading.gif
/iui/backButton.png
/iui/blueButton.png
/iui/cancel.png
/iui/grayButton.png
/iui/listArrow.png
/iui/listArrowSel.png
/iui/listGroup.png
/iui/pinstripes.png
/iui/redButton.png
/iui/selection.png
/iui/thumb.png
/iui/toggle.png
/iui/toggleOn.png
/iui/toolbar.png
/iui/whiteButton.png
/images/gym.jpg
/images/soccer.jpg
/images/online.jpg

NETWORK:
http://search.twitter.com/

In this example, you have added an explicit CACHE section to the manifest. The manifest can have different sections, but if it only has one, then it is assumed to be CACHE and can be omitted. The reason that you were explicit here is that you also have a NETWORK section. This indicates to the browser that anything coming from the domain specified (in this case search.twitter.com) should be fetched from the network and never cached. Since you are locally caching search results from Twitter, you certainly do not want the browser to do any more indirect caching of queries. Now, with this in place, the application will always load live tweets from Twitter, but it will also store those tweets and make them available to the user even when the user's device loses connectivity.


Summary

Web applications have come a long way since the days of Mosaic. Mobile Web applications have evolved even more. The days of WAP phones that only speak Wireless Markup Language (WML) are shrinking. Now you ask things of your mobile browsers that you don't even ask of their bigger desktop brethren. Offline functionality is one of those features. The standards specified in HTML 5 go a long way to making it simple for developers to offline enable their mobile Web applications. In the next article in this series you will look at how another HTML 5 standard, Web Workers, can dramatically improve the performance of mobile Web applications.


Download

DescriptionNameSize
Article source codeoffline.zip437KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source, Mobile development
ArticleID=493559
ArticleTitle=Creating mobile Web applications with HTML 5, Part 3: Make mobile Web applications work offline with HTML 5
publish-date=06292010