Digital Developer Conference: Hybrid Cloud. On Sep 22 & 24, start your journey to OpenShift certification. Free registration

Create offline mobile web applications with HTML5

HTML5 is a popular technology since its early days and for a very good reason. It brought desktop application-like capabilities to the web browser — not just on the desktop but also on mobile devices.

In this five-part series, we will take a look at some of the popular features that are part of HTML5. In each part, we will also write some code to showcase these features and see how they work on both desktop and mobile browsers.

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 tutorial, 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.

Prerequisites

In this tutorial, you will develop a web application to showcase the offline support in HTML5. The code in this tutorial uses core web technologies like HTML, Cascading Style Sheets (CSS), and JavaScript. To test the application on desktop and mobile, we recommend using the latest web browser versions.

Making your application work offline

The goal in this tutorial is for our web application to be as close to a native application as possible. To do this, we must support some of the features that native applications provide, and working offline is one of these features. Even for a data-driven web app that relies heavily on Internet connectivity to access web services, the app should gracefully handle offline mode instead of completely failing.

Web browsers have the ability to cache static resources. They use the metadata from the HTTP response headers to cache static assets like HTML, CSS, images, and even JavaScript. HTML5 introduced another approach to support caching and offline mode. It not only gives better control over the caching but it allows different versions of the same resource for offline and online mode if required.

All of this may sound complicated, but in reality, adding offline support to a web app is quite straightforward. You just need to create a cache manifest file and tell the browser to use this file for caching.

The cache manifest

They key file required for the offline support is the cache manifest file. This file contains information on what to cache and what not to cache. Listing 1 shows an example of a simple cache manifest.

Listing 1. Simple cache manifest

CACHE MANIFEST
#Version 0.1

offline.html
/scripts/app.js
/scripts/util.js
/styles/app.css
/images/gymnastics.jpg
/images/soccer.png
/images/gym.jpg
/images/soccer.jpg

This file lists all the resources that our application requires to function properly in offline mode. This includes HTML, CSS, images, JavaScript, etc. In our example manifest file, all of the URLs are relative to the cache manifest file. Let’s look at the directory structure of the application in Listing 2.

Listing 2. Text version of the directory structure for the web app

  Name
V images
    gymnastics.jpg
    gym.jpg
    soccer.jpg
    soccer.png
V scripts
    app.js
    util.js
V scripts
    app.css
  manifest.mf
  offline.html
> WEB-INF

All the files listed in the manifest file will be cached by the browser and will be available in offline mode.

One important thing to note in Listing 1 is the comment with version information. This is just a comment and not part of any specification, but we need to change the manifest file every time if the resource listed in it changes. For example, if there is a new version of the app.css file, the browser will not load the new file if the cache manifest file is the same. One way to tell the browser to reload resources is by changing the manifest file. When changing something in the manifest file, the browser will reload the resources. In this case you can just increment the version number.

Once you have the manifest file, update the app to inform the browser to use this file and enable caching. As you can see in Listing 3, just add the manifest attribute to the HTML tag of the web page with the manifest file name.

Listing 3. Offline-enabled web page

<!DOCTYPE html>
<html manifest="manifest.mf">
  <head>
    <title>Offline Support</title>
  </head>
  <body>
    <div>Offline Support</div>
  </body>
</html>

To test an app with offline support, deploy the app code to a web server, access the app once, then turn off the Internet connection and try to access the app again.

When using the cache manifest, the MIME type of the manifest file should be text/cache-manifest. If you are using the Apache web server, this can be done by using an .htaccess file, as shown below.

Listing 4. .htaccess file to set MIME type for manifest file

AddType text/cache-manifest .appcache

Now that you have seen the essential ingredients in creating offline web apps using HTML5, let’s look at an advanced example to explore more of the capabilities of offline web apps.

Advanced example

In the previous example, all the files used by our web app were static assets. Although it is good to be able to see all static content in offline mode, many of the web apps are data-driven. Most of the web apps are dynamic in nature, so they make network calls to retrieve data from the server. In this example, we will see how to handle offline mode for data-driven apps.

Listing 5. Check online/offline status

<!DOCTYPE html>
<html manifest="manifest.mf">
  <head>
    <title>Offline Support</title>
    <script type="text/javascript">
      const initialize = () => {
        let status = 'Offline'
        if(navigator.onLine){
            status = 'Online'
        }

        document.getElementById("status").innerHTML = status
      }

    </script>
  </head>
  <body onload="initialize()">
    <div id="status"/>
  </body>
</html>

In this code, we call the initialize() function on the page load. In this function, we detect whether the browser is online. If navigator.onLine is true, the browser is online; if not, then it is offline. Based on the status, update the status variable and display the state to the user.

Now that you know how to determine whether the browser is online, extend our example to make the HTTP call to retrieve some data only if the browser is online. For this example, we are using the REST Countries API, which provides various APIs to retrieve country-related information. Our example uses the API to retrieve a list of countries. You can look at their documentation for details.

Listing 6. Retrieve list of counties in online mode

<!DOCTYPE html>
<html manifest="manifest.mf">
  <head>
    <title>Offline Support</title>
    <script type="text/javascript">
      const initialize = () => {
        let status = "Offline";
        if (navigator.onLine) {
          status = "Online";
          retrieveCountries();
        }

        document.getElementById("status").innerHTML = status;
      };

      const retrieveCountries = () => {
        const xhr = new XMLHttpRequest();
        const url = "https://restcountries.eu/rest/v2/all";
        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
            displayCountries(JSON.parse(xhr.response));
          }
        };

        xhr.open("get", url);
        xhr.send();
      };

      const displayCountries = countries => {
        const ul = document.getElementById("countries");

        countries.forEach(country => {
          const li = document.createElement("li");
          li.appendChild(document.createTextNode(country.name));
          ul.appendChild(li);
        });
      };
    </script>
  </head>
  <body onload="initialize()">
    <div id="status"></div>
    <ul id="countries"></ul>
  </body>
</html>

As you can see, we updated the code to call the retrieveCountries() function when the browser is online. The function uses XMLHttpRequest to make an HTTP call and retrieve a list of countries using the REST countries API. Upon receiving a response from the server, we pass the list of countries to the displayCountries function, which then loops through the countries, adding them as a list item (li) to the countries unordered list (ul).

While you are handling offline mode gracefully by only making the network request when the user is online, you can enhance our app by using another HTML5 capability: local storage (see Listing 7). We discussed local storage in detail in Part 2 of this series.

Listing 7. Use Local storage

const initialize = () => {
        let status = "Offline";
        if (navigator.onLine) {
          status = "Online";
          retrieveCountries();
        } else {
          const localStorage = window.localStorage;
          if (localStorage) {
            const countries = localStorage.getItem("countries");
            if (countries) {
              displayCountries(JSON.parse(countries));
            }
          }
        }

        document.getElementById("status").innerHTML = status;
      };

      const retrieveCountries = () => {
        const xhr = new XMLHttpRequest();
        const url = "https://restcountries.eu/rest/v2/all";
        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
            const countries = JSON.parse(xhr.response);
            displayCountries(countries);
            storeCountries(countries);
          }
        };

        xhr.open("get", url);
        xhr.send();
      };

      const storeCountries = countries => {
        const localStorage = window.localStorage;
        if (localStorage) {
          localStorage.setItem("countries", JSON.stringify(countries));
        }
      };

In the code in Listing 7, you have updated the initialize function to handle offline mode. If the user is offline, it checks whether the browser supports localStorage, and if it does, it retrieves the stored list of countries. If there is a previously saved list of countries, you pass this list to the displayCountries function.

We have also updated the retrieveCountries function to call the storeCountries function after receiving the API response. The storeCountries function again checks whether the browser supports localStorage, and if it does, stores the list of countries for later use.

If the user is online, make the API call to retrieve the list of countries. If the user is offline, check for the locally stored countries list, and if it exists, use that data and display it to the user. Check the status to determine whether the browser is online on the page load. Also see if the status changes while the user is still on the web page. You can use online and offline events (see Listing 8) for a notification in case of any status change.

Listing 8. Event handlers

const initialize = () => {
  let status = "Offline";
  if (navigator.onLine) {
    status = "Online";
    retrieveCountries();
  } else {
    const localStorage = window.localStorage;
    if (localStorage) {
      const countries = localStorage.getItem("countries");
      if (countries) {
        displayCountries(JSON.parse(countries));
      }
    }
  }

  document.getElementById("status").innerHTML = status;

  document.body.addEventListener(
    "online",
    function() {
      document.getElementById("status").innerHTML = "Online";
    },
    false
  );
  document.body.addEventListener(
    "offline",
    function() {
      document.getElementById("status").innerHTML = "Offline";
    },
    false
  );
};

You have updated the initialize function and added event listeners for online and offline events. If there is a change to the browser’s status, these listeners will be called and the code will write the new status to the DOM. The cache manifest file needs to be modified, as shown below.

Listing 9. Modified cache manifest

CACHE MANIFEST
#Version 0.2

CACHE:

offline.html
/scripts/app.js
/scripts/util.js
/styles/app.css
/images/gymnastics.jpg
/images/soccer.png
/images/gym.jpg
/images/soccer.jpg

NETWORK:
https://restcountries.eu/

The manifest file can have different sections to inform the browser what to cache and what not to cache. In our example manifest file in Listing 9, you have a CACHE section to specify all files required for offline support that should be cached. In the NETWORK section, we specified to the browser that anything coming from the domain listed in this section should not be cached. By not specifying these sections, any resource listed by default in the manifest file will be cached.

Summary

Web applications have come a long way. Thanks to HTML5 features like offline support and local storage, web applications can provide the functionality, performance, and user experience similar to the desktop experience, if not better. In this tutorial you have seen how easy it is to make web apps work in offline mode. In the next part of this series, you will learn about another HTML5 feature, web workers, and how to use them to further improve performance and user experience.

Acknowledgements

This article was originally written by Michael Galpin and published on June 2, 2010.