navigator.onLine: here be dragons

When I took my cycling proficiency test a long time ago, the instructor asked us the following question:

When a driver sticks their arm straight out of their window, what are they signalling?

There is an official answer to this – the driver is turning right (in the UK the driver sits on the right side of the car). However, the answer taught to me by the instructor was far more realistic and entertaining:

It means the window is open

Some things are intended to be useful but are effectively meaningless. The onLine property of the window.navigator object and the accompanying online and offline events are to some extent another example of this. The spec defines the value of the onLine property like this:

The navigator.onLine attribute must return false if the user agent will not contact the network when the user follows links or when a script requests a remote page (or knows that such an attempt would fail), and must return true otherwise.

So the spec defines the behaviour that should result in a false value. But the inverse of this leaves a lot of room for the connection to be seriously broken. The spec doesn’t cover these, so if the value is true, that simply means that the user might be online. Let’s look at some of the stuff that will be the case even if onLine is true:

  • Some websites may have servers down so won’t respond anyway
  • Not all websites will perform equally well
  • The strength of signal could be awful, so although there is technically a connection, it might be unusable
  • There might be a captive portal intercepting every request, but nevertheless returning a response quickly that makes the device think it’s online
  • We might be on a content-filtered network, meaning that (for example) trying www.twitter.com works but just loads a page saying “Social media sites are not permitted”
  • It might be that some protocols are working and not others, eg DNS, email and IM are working, but HTTP is not
  • There may be an upstream break in the connection, such as if the device is on a wifi network that isn’t connected to the internet.

The spec also does not define how the user agent should determine whether the network connection is available or not.

But that doesn’t mean that navigator.onLine is useless. If it returns false, then you can be pretty sure you’re offline. Despite the lack of clarity on how the browser figures it out, there are certainly easy indicators that it can use – for example, if the device has no active network interfaces.

Communicating connection state

There are three ways I can think of in which you can communicate connection status to the user:

  1. Proactively: Before the user provides any input, change the interface to reflect the connection state, eg by greying out all options that require the user to be online, or by simply having a connection status icon on the UI somewhere
  2. In response to input: When the user chooses an option that requires a network connection, alert them immediately that the action cannot be completed
  3. In response to failure: When the user chooses an option that requires a network connection, attempt the request regardless of connection state, and if it fails, alert the user.

The first of these strongly implies equal confidence in the positive as the negative – since the user will see things sometimes greyed out, when they’re not greyed, they have confidence that they are online. The problem is that this confidence is not in any way supported by the spec or the implementation of navigator.onLine.

The other two are a lot better. If as a result of a user action, we need to do a network request, there’s no better way of determining the actual connection state than doing the request and seeing what happens. But since we can have confidence in a false value from navigator.onLine, it’s worth doing a quick check and failing early if the value is false. The important difference is that we don’t make any assumptions at all if the value is true.

Lessons learned

So, in short, it’s rarely a good idea to use navigator.onLine, or the associated online and offline events, to give users a proactive indication of connection state. Instead, it’s better to use it to fail early and quickly when the device has a high confidence of no network connection.

In the case of the events, that means if an online event is fired, we could use that to trigger a background process such as attempting to resend a queue of stored API calls, but should not use it to alter the UI since it’s quite possible that any request we wish to make will still fail.