Frank DENIS random thoughts.

Back and forward buttons considered harmful

The back and forward buttons are key buttons of any web browser since the web age. And they have always been seriously flawed.

By the time web sites were nothing but static content, it made plenty of sense.

But letting the user freely navigate through his history is fundamentally incompatible with dynamic content.

Going from page A to page B through a click on a link doesn’t quite have the same side effects than through the back and forward buttons.

In the later case, as long as the target page is still in the browser cache (minus some specific cases like the presence of an unload handler), this page will get rendered straight from the cache. Expiration rules for these resources are likely to be totally ignored.

Pushing the “forward” button may actually render a page whose content is completely outdated compared to the current page. This is terribly confusing, both to end users and to developers (what about inconsistent time-based security tokens?)

What users are instinctively expecting from the “back” and “forward” button is actually the previous and next pages. But they also expect the result to be up-to-date.

If an user changes his avatar, goes back to the previous page and sees his profile with an old avatar, something feels wrong. What is he going to do? Immediately hit F5 and reload the page.

This demonstrates that something is wrong. Back/forward buttons are broken by design. No one actually wants to walk through a list of previously rendered pages.

The only exception I can think of is when you accidentally leave a page while you were filling a form. But the proper way to deal with this is to ask confirmation before leaving the page, like any decent web browser already does.

The back + reload pattern is a conceptual nonsense, yet everybody does it, all the time.

The back and forward buttons introduce a couple of other issues that everybody constantly comes accross.

Like the fact that any page loaded through a POST request is bound to be submitted more than once, due to these infamous buttons. Any web developper has faced this milliseconds after his first web form has been put online. The browser-side band-aid (a confirmation box) doesn’t make things any better, and is just plenty annoying to the user just trying to get a few pages back.

Nowadays, the back and forward buttons as we know them don’t make any sense.

Neither does the reload button. Why should users have to ask for the content to be refreshed? This is gross, especially if you consider the fact that 99.9% of the times, hitting F5 actually yields the very same content. Hence we keep hitting F5 and F5 over and over again.

Browsers buttons are totally inadequate to any web site showing any kind of dynamic content. And even for static content, having no way to navigate through a web site without hitting the back and forward buttons is a blatant sign of a bad UI.

Almost every analytics tracking code is triggered when a page is loaded. I’ve yet to see a popular one that counts when a page is actually viewed. The “Page Views” metric is way off base due to back and forward buttons, in addition to adblockers.

Once and again, Javascript comes in handy in order to implement workarounds.

Manipulating the browser history plus updating the URI hash and loading the content through XHR is how we are forced to reinvent the wheel. It works like a charm, but it really shouldn’t be needed at the first place. Not in year 2010.

On a non-AJAXified web site, implementing back and forward buttons that are walking through URIs but not through old content can be achieved by observing the pageshow event.

This event is not as popular as onload. However, it’s extremly useful, as it is fired when a page is actually displayed.

Yes, even when the page has been reached through pushing the back and forward buttons.

A notable exception is, on iOS, when the browser (or an app embedding a webview) was put in the background and comes to foreground again. The pageshow event doesn’t seem to be fired as it probably should.

What can be done once this event is received? Well, the most obvious thing to do would be to reload the page, with something as trivial as:

var last_update = null;
var page_ttl = 1000 * 10;

function ping(timeout, cb) {
    var now = new Date().getTime();
    last_update = last_update || now;
    if (now - last_update >= timeout) {
        cb();
    }
}

window.addEventListener('pageshow', function() {
    ping(page_ttl, function() {
        document.body.innerHTML = '';
        location.reload(false);
    });
}, false);

If the user immediately pushes back / forward, the cached content is shown. This is a safe guard against the “oops, I clicked by mistake” case.

After 10 seconds, the button loads the cached page, and possibly updates the content (depending on the HTTP cache), as if a regular link had been clicked on.

Hiding the body happens to be necessary in order to avoid users to click a link, enter text or push a button on the cached page just before it’s being reloaded. This is especially relevant on mobile devices.

Of course you should avoid reloading content loaded through the POST verb at all costs. Use AJAX, at least for that. Seriously. And this doesn’t prevent you from saving what the user already wrote in form fields. sessionStorage is a perfect tool for such a task.