
Loading images offline
Images are a resource that almost all websites in the world today use. Just like your HTML, CSS, and JavaScript, you can cache images to be viewed offline with service workers. In this chapter, we are going to look at how to load images offline, as well as handling responsive images.
Getting ready
To get started with service workers, you will need to have the service worker experiment feature turned on in your browser settings. If you have not done this yet, refer to the Setting up service workers recipe of Chapter 1, Learning Service Worker Basics. Service workers only run across HTTPS. To find out how to set up a development environment to support this feature, refer to the following recipes of Chapter 1, Learning Service Worker Basics: Setting up GitHub pages for SSL, Setting up SSL for Windows, and Setting up SSL for Mac.
How to do it...
Follow these instructions to set up your file structure:
- First, we must create an
index.html
file as follows:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Offline Images</title> </head> <body> <main> <p>Registration status: <strong id="status"></strong></p> <img src="packt-logo.png" alt="logo"> <main> <script src="index.js"></script> </body> </html>
- Now we have to create a JavaScript file
service-worker.js
, in the same folder as theindex.html
file, with the following code:'use strict'; var version = 1; var cacheName = 'static-' + version; self.addEventListener('install', installHandler); self.addEventListener('fetch', fetchHandler); function installHandler(event) { event.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll([ 'index.html', 'packt-logo.png' ]); }) ); } event.respondWith( fetch(event.request).catch(function() { return caches.match(event.request); }) );
- Create a JavaScript file called
index.js
, in the same folder as theindex.html
file, with the following code:'use strict'; var scope = { scope: './' }; if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js', scope ).then( function(serviceWorker) { printStatus('successful'); }).catch(function(error) { printStatus(error); }); } else { printStatus('unavailable'); } function printStatus(status) { document.getElementById('status').innerHTML = status; }
- Download an image file and save it in the same folder as the
index.html
file. In this example, I am calling itpackt-logo.png
. - Open up a browser and go to the
index.html
file: - Open up Chrome Developer Tools (Cmd + Alt + I or F12), select the Network tab, and click Offline:
- Refresh the page by pressing Cmd + R or F5, and you will see the image looks the same as it did online.
How it works...
In the index.html
file, we are linking the image we have downloaded inside an img
tag:
<body> <p>Registration status: <strong id="status"></strong></p> <img src="packt-logo.png" alt="logo"> <script src="index.js"></script> </body>
In the service worker script file, we add our offline page to the cache when we install the service worker. In the first few lines, we specify the cache version and the URL for the offline page:
var version = 1; var cacheName = 'static-' + version;
The event listener for the install event calls the waitUntil
function, where we cache index.html
and the font file, in our case, webfont-serif.woff
. The cache.addAll
function takes an array of files to be cached:
self.addEventListener('install', function(event) { event.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll([ 'index.html', 'packt-logo.png' ]); }) ); });
When we reload the page, after it is set to go offline, the fetch event gets fired, retrieves those two files from the cache, and sends them along with the response:
self.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request)); });
Now, the page will be displayed as it was online.
There's more...
If we were to develop our website following a mobile-first strategy, having responsive images would greatly benefit it. Let's look at how we can achieve this.
Handling responsive images
There are a number of ways to enable the responsive behavior for images. One of the older methods (not recommended) is by simply scripting, but this leads to a couple of problems. First, if a script determines which image to download, but the script itself is loaded after the images specified in the HTML have been downloaded, you may potentially end up with two downloaded images. Second, if you don't specify any image in HTML, and want to load only the image defined by the script, you'll end up with no image at all for browsers that have scripting disabled.
Hence, we need a better way to deal with responsive images. And thankfully, there is one! The recommended way is to use:
srcset
sizes
picture
The srcset attribute
Before we explore how srcset
is actually used, let's understand a few terms.
Device-pixel ratio
The device-pixel ratio is the number of device pixels per CSS pixel. Two key conditions contribute to the device-pixel ratio:
- Pixel density of the device (number of physical pixels per inch): A high resolution device will have a higher pixel density and hence, for the same zoom level, it will have a high device-pixel ratio compared to a lower resolution device. For example: a high-end Lumia 950 phone will have a higher resolution than a budget Lumia 630 phone, and therefore it will have a higher device-pixel ratio for the same zoom level.
- Zoom level of the browser: For the same device, a higher zoom level means a higher number of device pixels per CSS pixel, and hence a higher device-pixel ratio. For example, consider this figure:
When you zoom in on your browser (Ctrl + +), the number of CSS pixels for your div
remains the same, but the number of device pixels it occupies increases. So, you have a higher number of device pixels per CSS pixel.
When you want to display separate images (or usually, a separate asset of the same image) based on the device-pixel ratio, you'd go with a basic srcset
implementation:
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />
The x
descriptor in the srcset
attribute is used to define the device-pixel ratio:
- For a device-pixel ratio of 1, the
image-src.png
image will be used. - For a device-pixel ratio of 2, the
image-2x.png
image will be used.
The src
attribute is used as a fallback for browsers that do not yet support srcset
implementation.
This works well. Using the x
descriptor, you'll always get the same image on devices with a similar device-pixel ratio—even if this means that you get the same image on a 13.5-inch laptop, and a 5-inch mobile phone, which both have the same device-pixel ratio.
The sizes attribute
The actual implementation where you'd want a different-sized image (different height and width) on different screen sizes is accomplished by using the sizes
attribute along with the w
descriptor of the srcset
attribute.
Say you want the image to be viewed in half of the viewport width. You'll type:
<img src="image-src.png" sizes="50vw" srcset="image-src.png 1x, image-2x.png 2x 400w">
The picture element
As we saw in the previous section, the picture
element is used when you want to show a different image depending on the rendered size of the image. The picture
element is a container, which contains other elements that control the image to be downloaded:
<picture> <img src="image-src.png" sizes="50vw"srcset="image-src.png 1x, image-2x.png 2x 400w"> </picture>
At runtime, the srcset
attribute or the <picture>
element selects the most appropriate image asset and performs a network request.
If you want to cache an image during the install step for the service worker, you have a few options:
- Installing a single low-resolution version of the image
- Installing a single high-resolution version of the image
It is ideal to limit the amount to two or three images in order to preserve memory.
To improve the load time, you may decide to go for the low resolution version at the time of installation, and you would try to retrieve higher resolution images from the network when the page is loaded; however, in the case that the high-resolution images fail, you would think you can easily fall back to the low resolution version, but there is one issue.
Let's assume we have two images:

Here is the markup for an srcset
image:
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />
On a screen with a 2x
display, the browser could opt to download image-2x.png
, if we are offline, then we could catch this request and return the image-src.png
image instead if the image is cached, the browser may expect an image that considers the extra pixels on a 2x
screen, therefore the image will appear as 200 x 200 pixels instead of 400 x 400 pixels. The only fix is to set a fixed width and height on the image:
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" style="width:400px; height: 400px;" />
We can take the same approach to srcset
.
No width or height set:

Height and width set:

If you want to unregister the service worker, you can head to the Developer Toolbar in Chrome, and click the Unregister button in the Service Workers section, as shown in the following screenshot:

If you want to find out the resources stored in the caches, you can do so by opening Developer Tools and looking at the Resources tab:

If you are using Firefox Nightly, you can view the caches by opening up Developer Tools and looking at the Storage Inspector:
