Honza Malý4.1.2018Back

Offline in the browser (part 1): Detection, AppCache

Being offline is something that most people want to avoid as much as possible. So, unless you are in search of peace for meditation and sorting out your thoughts, the no-connection icon and an error message in the browser is not exactly something you rank among the brightest moments of everyday work.

You can, however, turn offline into an advantage if you make your website or app ready for it. Imagine the feelings of a user if one in ten tabs in the browser, being your tab, is still working in an offline regime even when there’s no connection. Is there any better way to make the user realize that you take care of his or her comfort?

Be careful, though, because the correct settings and the right behaviour in the offline regime are a bit tricky in today‘s world of browsers. There is a multitude of ways and each has its pros and cons. And not all of them are always fully supported by all browsers.

In this article, you are going to find out which types of websites and apps are suitable for an offline mode. We are also going to describe the most frequent method these days: Application Cache.

When offline comes in handy

Offline is handy with apps designed to “do stuff”, meaning those in which you perform some tangible work. Typing texts, planning tasks or filling in questionnaires.

“Do-stuff applications” are characterised by the following criteria:

  • most of the content of the work you do does not require being online;
  • their state can be cached for some time and stored later.

When is offline inappropriate? For sure if it’s a “look-up stuff” application, i.e. search engines and information portals. Logically, those are websites requiring almost constant online status. One of the few examples of using offline would be to cache search results for later use after the connection goes down. But considering the fact that this mode has not been implemented by Wikipedia or Google – the kings of all “look-up stuff” portals – highlights two fundamental facts:

  1. it is not easy to implement,
  2. deciding how the whole thing should work is not a piece of cake.

Just because we want it with no particular goal is not a reason to have an offline mode in an app. If you can’t image a real scenario of how it should be used, don’t spend any time on it.

How we got to that conclusion

Our first encounter with offline support was when a client asked for a one-page JavaScript web app (a simple React + Redux) to continue working on a tablet even if the user is not online. In the offline regime, the app would cache all modifications and only sync when reconnected.

From the data point of view, it is not so difficult. Thanks to Redux, I have an excellent overview of the app status and I can postpone caching in until a later status. Of course, it would turn into a problem if I had one set of data being modified by more than one user – what if one of them is online and modifying the data? Some kind of a sync tactic would have to come into play. The app was simple, though, (one session per user) and we ignored that problem.

So, we have a pretty good idea of how it should work, let’s move on to implementation. The offline detection is all it takes. But how do you really know that a browser is offline? Will AppCache be of any help? What happens if a user hits F5 to refresh a page?

Now let’s move on to the “how-to” part where we describe the individual steps.

Offline detection in a browser

What may seem to be a simple matter at first sight is in fact not easy at all.

First, there are properties of the DOM model that inform you whether you are online or offline. You can find a concise description at MDN. You can use:

  • navigator.onLine - tells you the current status of the browser,
  • online and offline events; you can simply add a handler to them activated by a change in the status.

According to caniuse.com , these properties are supported on a great scale. Problem solved? Not quite.

The main problem you run into is that the browser itself does not run any regular tests of its online or offline status. The browser usually doesn’t find out until a network requirement is called, which may be a bit too late if the application relies on its fulfilment.

During my investigations, I ran across the Offline.js Library and was inspired by how it works. The library is based on a rough but elegant method – sending a short test request and detection of success. This is what it looks like in principle:


Exhibit: how rough online / offline detection works.

If you simply need to know (like I did in this example) that AJAX went offline during this or that request or that the request was performed correctly (and the status is online then), you do not have to use Offline.js. You can simply write the code yourself. See my code (ES6):

let offline = false;
let xhr = new XMLHttpRequest();
xhr.open(‘HEAD’), OFFLINE_TEST_IMAGE + "?s=" + generateRandomId(), false);
try {
  xhr.send();
} catch(e) {
  offline = true;
}	


Why that complicated line (at least at first sight) giving the path to the request? OFFLINE_TEST_IMAGE represents the path to the testing image (for example: favicon.ico - size does not matter, this is only a HEAD request) and the function generateRandomId() creates a random string which helps us prevent the browser from caching this query.

Such an easy piece of code then creates something I call an OfflineBlock. It is a method called by all asynchronous requests and one that will run the AJAX query only if offline == false.

This solution is trivial and works great but it has one disadvantage. If the user refreshes the page, he or she will lose everything, getting an error message.

This is why this solution is naïve and requires an educated user. In our implementation, we have highlighted the offline status in the app. Once you click on the sign, you get a dialog offering you a “retry” of the last action but using current data (to make sure the app does not rewrite the modifications made so far). What helps a great deal is that the application status is available all the time in the Redux status tree.

Application Cache

The name may be a bit misleading. It denotes a method you use in HTML5 to control the cache settings and browser offline behaviour. I mean, that’s how it should work but as you are going to see in a bit, covering all scenarios (and covering them simply) is pretty impossible.

If you want to work with AppCache, please read the specification really carefully. Pay attention to detail. It will help you avoid swearing and endless pondering why the thing is not working the way it should be.

Note: For now, just ignore the “deprecated” warning all over the web. AppCache is really considered obsolete, and for good reasons. But a meaningful replacement simple does not exist at the moment (November 2017).

The main principle lies within the definition of the special AppCache Manifest file, which is any file that your server sends along with mime-type: text/cache-manifest – by the way, if it is not the case, this is how you turn it on in Apache in .htaccess:

#all files .appcache - the suffix may be different
AddType text/cache-manifest .appcache 	

 

You have to append this file to your site like this:

<html manifest="/manifest.appcache">	

 

As your HTML page downloads under a specific URL, a link is created in your browser: a manifest file becomes available for the given URL. This file describes how will resources be fetched on that page.

AppCache file structure

The file must start with a text.

CACHE MANIFEST	

Several optional sections are going to follow now. A section always starts with the section name in capitals and a colon. Below the section heading, you write the individual document addresses, each of them on a new line.

CACHE is the first section: It lists the files to be cached, i.e. those which will also work offline. The addresses are relative to the location of the file with the manifest (an important detail!) Further on, it is not necessary to list pages in which the manifest is given in HTML – those are associated automatically.

CACHE:
js/index.js
css/styles.css

 

Another section, NETWORK: It says that this file always has to be downloaded from the server.

NETWORK:
data/live.json

 

The last section, FALLBACK, serves to determine the backup file in case the network requirement for the former file fails. It is the only element composed of a slightly different syntax: the source file and the replacement file separated by the space character:

FALLBACK:
images/header.jpg images/header_offline.jpg

Naturally, the back-up file will be cached as the manifest is being processed to make sure the whole thing works.

Problems with AppCache

Jake Archibald wrote a great piece on the AppCache issues in his article Application Cache is a Douchebag. And this was in 2012, my friends! We are going borrow some of the most fundamental problems from Jake’s article – those you must be able to resolve.

  • Files are being cached all the time, even if you are online. So, it’s not appropriate where one file may have different contents online and offline. What I gather is that the best practice is to have sources for online / offline in different locations or at least bearing different file names.
  • Uncached sources will not download on a cached page even if you are online. This implies that within the previous bullet point, it really pays off to separate the online and offline applications, giving it some thought.
  • Application Cache only updates if the manifest file is modified physically. This can be treated with a comment in the manifest that regenerates automatically during build.
  • Application Cache is not the cache in the browser. The browser keeps applying its own cache based on HTTP headers. It can often cache contents that you have specified using the NETWORK section independently.

AppCache API

JavaScript provides access to AppCache API and that is where things start to get more interesting. First thing, the status is easy to detect with regards to AppCache:

window.applicationCache.status == 2 
	// checking - checks the latest version of the manifest

AppCache also triggers several events as and when the applicationCache.status is being modified - you can listen to them and link your next steps to them.

Finally, there are functions available to manipulate the AppCache:

window.applicationCache.update() 
	// generates an AppCache update
window.applicationCache.swapCache() 
	// update for a new AppCache, if available (this would only succeed after the reload)

Conclusion

We have covered the reasons to provide offline support to your web project. We have discussed offline detection and Application Cache. In our next edition, we are going to take a look into the guts of Service Workers, which is still kind of a “music of the future” but one with great capabilities.

Honza Malý
maly@kurzor.net
+420 722 211 443
Honza specializes in interaction design and website development.

Part of Series: Go Offline

Offline in the browser (part 2): Service Workers

Wouldn’t it be great to control the application cache directly, via a programmable interface? That's what Service Workers are for.

Honza Malý25.4.2018

Send Us a Message