Showing Different Content To Offline Visitors With JavaScript

February 24, 2019

Note: The JavaScript examples found in this article use ES6+ code. I recommend transpiling this code to ES5 using Babel to improve support for older browsers.

Here’s a cool trick I discovered the other day. I’m using a ServiceWorker to make some pages on this website available to offline visitors, including my contact page. However, this page has a form to contact me, and I realised this form would not work if a visitor was offline. Since I didn’t want to make this page unavailable to offline visitors, I had to come up with an alternative solution.

A quick Google search led me to this article by David Walsh explaining the navigator.onLine property. It has been around since at least IE8 and has excellent browser support. This property returns either true or false depending on the online status of the visitor. It even comes with offline and online events, to make changes when the online status of a visitor changes. Perfect!

The approach

I wanted to keep things simple, and I decided to introduce the [data-online-only] and [data-offline-only] attributes. These attributes can be used to show content only if the visitor has the corresponding online status. Here’s a quick example:

<div data-online-only>
    The visitor is online. You could show a contact form here.
</div>

<div data-offline-only>
    No internet connection. Maybe show an email address instead?
</div>

Looking good!

How do we implement this?

To make this work, we only need a few lines on JavaScript. However, it would also be a good idea to hide offline-only items by default:

[data-offline-only] {
    display: none;
}

This CSS prevents offline-only content from being visible while the JavaScript is loading. We default to showing online content and hiding offline content—after all, the vast majority of your visitors will have an active internet connection.

With that done, it’s time to add the actual functionality. First of all, let’s find all the online-only and offline-only elements on the page:

const onlineItems = document.querySelectorAll('[data-online-only]');
const offlineItems = document.querySelectorAll('[data-offline-only]');

We declare two variables, onlineItems and offlineItems. Each variable contains a NodeList of elements matching the selector.

Next, let’s write two small functions which show or hide elements based on the online status of the visitor:

const toggleDisplay = (elements, value) => {
    elements.forEach(item => {
        item.style.display = value;
    });
}

const showItems = () => {
  toggleDisplay(onlineItems, navigator.onLine ? 'block' : 'none');
  toggleDisplay(offlineItems, navigator.onLine ? 'none' : 'block');
};

Note: In the past I’ve had issues on older browsers with using forEach() on a NodeList, even after including a polyfill. If this is the cause for you, try turning the NodeList into an array before using forEach(), for example: [...onlineItems] or Array.from(onlineItems).

The first function, toggleDisplay, is a small helper function to set the CSS display attribute for all items in a NodeList. It takes two parameters, elements and value, and sets the display value to the passed value for each item in the elements parameter.

The second function, showItems, is where the magic happens. It calls our helper function twice, once for online-only items and once for offline-only items. In each case it passes a value based on the online status of the visitor. For example, if a visitor is online all online-only items will get a display value of block and all offline-only items will get a display value of none, and vice versa.

There’s only one step left to do now, which is initializing our code and adding event listeners:

if (!'onLine' in navigator) {
  return;
}

showItems();

window.addEventListener('online', showItems);
window.addEventListener('offline', showItems);

First, we check for the presence of the onLine property in navigator. Browser support is very good for this, but it’s better to be safe than sorry. If this property isn’t available, we simply bail early. The CSS we added earlier ensures that in this case we’ll only see the online-only content—a sensible default in my opinion.

Next up, the only thing left is initializing our code. We run the showItems function on page load, which means that we’ll show and/or hide items based on internet connection status. We also add event listeners for the online and offline events. As you might have guessed, these events trigger when the visitor’s internet connection changes status. By hooking our function into these events, we can show the appropriate content even when the internet connection changes while visiting the page.

And that’s it! Now, using the [data-online-only] and [data-offline-only] attributes, we can selectively show or hide content based on the internet connection of our visitors.

Other ideas

Now, this is of course a very simple implementation of this feature. However, there’s definitely other things you can do with this. For example, how about replacing YouTube embeds with descriptive images or text? Or perhaps adding an is-offline class to the body and styling specific items based on this? I’m sure there are plenty more cool applications—if you think of anything, let me know on Twitter!

Also, if you did enjoy this article or found it useful, please let me know on Twitter as well! I’m still very new to this blogging thing, and I’m experimenting with different types of articles. Any type of feedback or constructive criticism is much appreciated.

Do you have any questions or comments about this post? Tweet me at @danielpost! I am also available for hire!