One of the most interesting features of HTML5 is support for application cache. Every Web browser has a built-in cache that stores recently visited Web pages. This means the same pages and assets will load faster the next time the browser returns. At every subsequent call, the browser checks the timestamp to determine whether to reload the assets from the server or serve the cached versions. This simple caching mechanism requires a network connection to function and only works with resources that have already been downloaded.
Application cache improves on traditional browser caching significantly. It enables the browser to prefetch
Web pages/assets, by preempting the user’s requirements, and to make all cached resources available in offline mode.
A first look at the cache.manifest file
The crux of the system is the cache manifest file. This lists all assets that need to be cached and details how the resources should be dealt with when the device is offline. The cache manifest file is referenced by providing its relative or absolute URL in the manifest
attribute of the html tag. When a user visits a page that declares a manifest, the browser will check if there have been any changes since the last visit and, if so, it will re-download all the resources specified and cache them anew.
The manifest file needs to be in the same domain. Let’s take a look at a minimal example:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!DOCTYPE HTML> <html manifest="cache.manifest"> <head> <title>Test</title> <script src="js/scripts.js"></script> <link rel="stylesheet" href="css/styles.css"> </head> <body> <p>This is a document</p> <img src="img/image1.jpg" alt="image 1"/> <img src="img/image2.jpg" alt="image 2"/> </body> </html> |
The file can have any name or extension, what is crucial is that it needs to be served with a text/cache-manifest
MIME-type. Depending on the Webserver used, this is done by adding the correct mime-type for the chosen extension. This is an example for Apache using an .htaccess file:
1 |
AddType text/cache-manifest .manifest |
The Cache manifest file in its simplest form is a text file starting with CACHE MANIFEST. Each directive refers to a file and must be on a new line. Empty lines are ignored and comments start with a hash symbol (#).
1 2 3 4 5 6 |
CACHE MANIFEST # v.1 css/style.css js/scripts.js img/image1.jpg |
The HTML document that references the file is implicitly cached, so it does not need to be added to the list. In this example, the cache manifest will trigger the caching of the html file and all assets listed. Note that we have intentionally not listed one of the image assets included in the previous example.
When the browser finds a manifest file it starts loading all assets listed and on subsequent calls, it will load the page straight from the cache. After the page is loaded, if a network connection is available, it will check if the manifest file has changed on the server and, if so, trigger a cache refresh. What will happen in our example is that at first load the page will be loaded correctly and cached, but on second load the page will be requested straight from the cache so the second image will not be loaded, even if a network connection is available, because it was not listed. This example shows a few very important implications that need to be taken into account:
- A cached page is “frozen” in time – thus, the browser will ignore any changes to any file unless the cache manifest file itself is modified.
- All resources listed in the manifest file should be available – if just one is invalid the whole caching process will fail.
- All resources listed will be downloaded in the background. This means that processing all entries might take time and consume bandwidth. This is particularly important on mobile experiences.
- Cache manifest files should be transferred with a no-cache instruction because they will otherwise be cached just like any other file by the browser and the cache refresh will not be triggered
Mobile browser compatibility with application cache
The latest version of most mobile browsers, including Safari, Android, Chrome, Silk, Blackberry, Opera mobile and Firefox support the W3C standard for application cache, but not all. See Maximiliano Firtman’s browser compatibility table at mobilehtml5.org for more details:
A deeper look at the cache.manifest file
Now let’s take a look at a more complex example of a manifest file to understand how to deal with the above notes:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CACHE MANIFEST # v.1 CACHE: css/style.css js/scripts.js img/image1.jpg NETWORK: * FALLBACK: /img/ img/placeholder.jpg |
In its simplest form the cache manifest is a list of assets and if no section name is provided is assumed to be a list of cached items. There are three kinds of sections that are allowed in a cache manifest document in any number or order:
CACHE
– this specifies a list of assets that will be cached. Each needs to be explicitly listed, no wildcards are allowed.NETWORK
– this specifies assets that will be allowed to be requested through a network connection. Wildcards can be used so in our example we use * to allow the browser to download any resource that is not already cached (if a network connection is available, of course).FALLBACK
– this details the alternative resources, when the preferred resources are unavailable, e.g. when the network or server is unavailable. Wildcards can be used in the first part and resources specified in the second part will be implicitly cached. In our example “image 2” will be downloaded if a network connection is available, but replaced with a placeholder when offline. Please note that the URL is not rewritten.
JavaScript API
To programmatically access the application cache, the browser exposes a window.applicationCache object. Querying the status object will return UNCACHED
; IDLE
; CHECKING
; DOWNLOADING
; UPDATEREADY
or OBSOLETE
– each response is assigned a numerical value, as illustrated below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// get The application cache object var appCache = window.applicationCache; //The <a href="http://dev.w3.org/html5/spec/single-page.html#concept-appcache-status" target="_blank">status</a> object returns a constant that represent the current state of the cache: switch (appCache.status) { case appCache.UNCACHED: // UNCACHED == 0 return 'UNCACHED'; break; case appCache.IDLE: // IDLE == 1 return 'IDLE'; break; case appCache.CHECKING: // CHECKING == 2 return 'CHECKING'; break; case appCache.DOWNLOADING: // DOWNLOADING == 3 return 'DOWNLOADING'; break; case appCache.UPDATEREADY: // UPDATEREADY == 4 return 'UPDATEREADY'; break; case appCache.OBSOLETE: // OBSOLETE == 5 return 'OBSOLETE</a>'; break; default: return 'UKNOWN CACHE STATUS'; break; }; |
When the user visits a mobile site/app, which declares a manifest, the following events will be fired by the applicationCache as it ascertains the need to download/update cached resources. Actions can be bound to these events to keep the user informed.
checking
fires when browser starts checking if cache manifest file is changed.noupdate
is fired after ‘checking’ if no change is detected.downloading
fires when browser finds an update, or downloads for the first time.progress
fires during download of assets.cached
fires when resources have been downloaded and added to the cache.updateready
fires when downloading of new assets has completed.obsolete
is fired after ‘checking’ if the manifest file returns a 404 error (file not found, possibly temporarily) or 410 error (Website not found, probably permanent), so cache is being deleted.error
is fired when an error (e.g. 404 or 410) with the site or manifest file leads to the download/update being aborted.
The applicationCache object provides these methods to ensure that the cached resources are up-to-date:
appCache.update()
forces the browser to check if the manifest file has changed.appCache.swapCache()
makes the new cache available at next refresh. The swapCache method should occur whenever a new cache is available:
1 2 3 |
appCache.addEventListener</a> ('updateready', function(e) { this.swapCache(); }); |
Please note that due to the way the application cache works the page would need to be refreshed in order to see any change. Calling
1 |
swapCache() |
does not actually reload or change any assets in the current page, but simply makes sure that the new resources will be used at the next refresh.
Things to note
Application cache can be very useful, but it should be used with caution, here are some aspects to be taken in consideration:
- Application cache does not replace the normal browser cache, it’s an additional cache. Thus it’s important to set correct expiry dates for assets. It is especially important to make sure the manifest file itself is not cached (or if it is cached, to make sure it has a very short expiry date).
- The Cache will be available only after a page refresh because the browser will serve the cached version of a page before checking for changes in the manifest.
- The browser provides no indication that a page comes from the cache so it’s usually good practice to use the JavaScript API to check for cache refreshes and prompt the user to refresh the page in case of changes.
- If any of the assets listed in the cache fails to download the whole cache will be invalidated and the cache deleted.
- Assets linked by stylesheets are not implicitly cached so they should all be listed in the cache. This makes the application cache not really suitable with responsive design techniques.
- It’s normally not desirable to cache JSON calls (JavaScript Object Notation). In my experience the best approach is to list JSON calls in the NETWORK section and always save successful responses in the localStorage. This guarantees that JSON contents will always be fresh without triggering a whole cache refresh.
- When using a CMS the best option is to generate the manifest file dynamically. In these cases it’s important to make sure that the generating script provides the correct MIME type.
- Browsers impose size limits on caches, especially on mobile, and might ask for user permission before storing the cache. iOS for example has a 5MB limit, which can be increased upon user permission, but no file bigger than 5MB can be stored in the cache.
- On iOS it is not possible to cache video files, because they are not played by the browser, but by the external QuickTime module, so even if they can be downloaded and cached successfully they will not play offline.
- The browser does byte-to-byte comparison to determine if the cached manifest file is changed so it’s not enough to re-save it to trigger a cache refresh. It’s necessary to change the contents of the file which is why it is handy to have a comment with a version number in the file.
- The cache will be stored only after all assets have been downloaded and browsers do not give any indication of the downloading process. This means that the user may leave the page without knowing that the caching process is incomplete, which will invalidate the whole thing.
- Cache manifest enabled pages might result in significant use of bandwidth, so should be used carefully on mobile connections.
Further reading
- A beginner’s guide to using the application cache – Eric Bidelman, HTML5 Rocks.
- Let’s Take This Offline – Dive into HTML5.
- How to control interaction with the Application Cache? – James D Bloom.
- Debugging HTML5 offline application cache – Jonathan Stark.
- Using the application cache – Mozilla development network.
- HTML5 offline application cache Apple developer site.
- Application cache is a douchebag – Jake Archibald, A list apart.