Maintenance free unsupported browser notification
Tags: javascript, webdev, tutorial
Published: 2020-10-01 10:53:06

The times of Internet Explorer are over. These days we often target only evergreen browsers. However it might be tricky to let users of unsupported browsers to know that hey, use a newer browser.

A common way to do this kind of notification is to look at user agent strings and maintain a list of browsers. This easily results into somewhat verbose code, and keeping the list up to date is PITA.

## Can we do better?

In typical application these days we have a bootstrap phase. This is where all the required parts of the app are initialized and a final command to hydrate the DOM is executed.

This is a point where we can be mostly confident that if all these steps succeeded it is likely the browser is supported.

But how to implement a lightweight solution that is free of maintenance?

There are two things that we need. First we need the notification element that is always rendered to HTML. This element contains the message displayed to user, but by default is hidden via CSS. This ensures we do not display the notification immediately upon page load and then turn it off. It is better to only show the message once we are certain that the browser failed our test.

The second part is the tech to trigger the display of the notification. For this I suggest a simple trick: add a class to the root HTML element. This logic should be applied with both fail and success paths.


### Default static HTML

1. Make sure your HTML always has a class defined in the root element: `<html class="">`
2. This makes sure we can use a simple and perfectly supported `document.getElementsByTagName('html')[0].className += ''` to add a class.
3. For notification component have default style to hide it: `#unsupported-browser-notification { display: none; }`
4. You can have the notification logic styles directly in the HTML `<head />` instead of separate CSS.


### The path of fail

1. The first `<script />` element on the page to guarantee our code executes.
2. Use `setTimeout` with a second or two delay which will always add to `className` of the root `<html />` element.
3. The class can be named something like `unsupported-browser` if you wish to be clear of the intent.
4. For notification component have style `html.unsupported-browser #unsupported-browser-notification { display: block; }`

With this implemented you should see the message always appearing.


### The path of success

1. To the end of bootstrap phase add code that adds to `className` of the root `<html />` element.
2. The class can be named something like `bootstrap-success`.
3. For notification component have a style **after** the unsupported style: `html.bootstrap-success #unsupported-browser-notification { display: none; }`

By always only adding to `className` we avoid race conditions.

With this implemented you should no longer see a message ever appearing in a supported browser. Unsupported browsers that don't even bootstrap will instead see a notification that their browser is not supported.


## What should we tell the user?

The notification should begin with that the user's browser is unsupported. However there may be times that this notification gets triggered incorrectly, for example, by publishing a broken bundle to production for whichever reason.

So here is a sample message:

> You are using an old unsupported browser. Update it or install another browser.
>
> **I use a new browser**: sorry for the inconvenience, we fix the problem as soon as possible. Contact us to let us know if the problem has persisted a long time.

This way we cover the both possible cases: user actually having an older browser, or us having a bad day.


## Downsides

A possible downside of this technique is that there are browsers that fall "in between" fully supported and clearly unsupported. The browsers in between will succeed to bootstrap, but will fail later when using a new browser feature that is not in the supported browser version range.

The only way around this would be to have the bootstrap phase depend on the latest and greatest features, or purposefully detect for those.

Another downside here is that the code is fragmented. This is nothing new in the frontend space, however a modern trend in frontend development has been to have everything related in one place for perfect clarity. People also like to use only one style of code instead of relying on vanilla HTML/CSS/JS solutions.


## The good parts

The code needed for this solution is minimal!

1. `<html class="">`

2. ```html
<script>
setTimeout(function() {
    document.getElementsByTagName('html')[0].className += ' unsupported-browser'
}, 2000)
</script>
```

3. ```html
<style>
#unsupported-browser-notification { display: none; }
html.unsupported-browser #unsupported-browser-notification { display: block; }
html.bootstrap-success #unsupported-browser-notification { display: none; }
</style>
```
4. The notification element in HTML.
5. At end of bootstrap: `document.getElementsByTagName('html')[0].className += ' bootstrap-success'` (or use `document.documentElement.classList`)


In addition this is as maintenance free as it gets. Over time more and more old browsers will start to fail as newer JS syntax is no longer being transformed in your bundles. You can also optionally do feature checks before applying the bootstrap success class, however that would need maintaining and might not be worthwhile. It all depends on how much user impact you have, and how many of them fall to the "in between" space of almost fully working.

Finally please let me know if you think this simple and minimal solution has more downsides or failures than I bring up here!