Preserving the back button on javascript based websites

Posted at February 22, 2012

We’re building more and more javascript rich websites. The more interactive it gets, the less it looks like a website at all. But we can’t ever forget were still building websites and we need to preserve a web based on navigating through links.

What it was designed for

A traditional website has different pages, you can navigate through the website by clicking on pages. Each page has it’s own unique url by which the page can be accessed directly. Pages are pretty static and the page mapped to an url doesn’t change at all over time, with the exception of updated content.

What we need

When you’re thinking about an application you’re not thinking about pages at all, but how can we still define different parts of the app? States are a possible answer, just like an url of a page the state is linked to a specific ‘interface’ which is part of the whole app. Note that applications are often more dynamic in nature than static websites. You can reflect all changes in the interface into unique states or just really big ones, but this depends on the nature and the size of your application. Otherwise it’s up to the developer which changes should be in the state and which shouldn’t.

How can we use it?

With websites shifting further away from static websites and acting more like applications we have to do some tricks to preserve old functionality like the back button and the bookmark button (and even the url pointing to the state/page). Note that all these tricks are of a hacky nature because we use them not in the way there were intended, however you can only row with the oars you got (an old dutch saying, we can only use the tools we have in the limited web environment). All the tricks use the url to map some sort of state to the interface.

When you’re building big stuff in javascript you want to prevent new pageloads as much as possible because this kills all your running scripts. But it’s pretty hard to change the url without a new pageload, in fact the’re are only a few methods to do so:

  • Save the state after the hash in a url.
  • Use the HTML5 feature pushstate to set the url without reloading.

Changing the hash

It’s really easy to change the hash. Because as of today most navigation is based on clicking, you can just point the href of an HTML element to whatever you want.

click me!

When someone clicks the link the hash will get set to my-awesome-state. You can also set it directly in javascript by accessing a global property.

window.location.hash = 'my-awesome-state';

When you use hash changes to reflect changes you can watch the hash by using the onhashchange event. This works in most modern browsers (but not in IE6 and 7). If you also target older browsers you could check if the hash has changed at an interval (to simulate the event). You should never watch your hash links for clicks because this breaks every way of navigating (the back button for example) except physically clicking on the link!

The problem with hashchange is that if you combine this technique with AJAX you would have to make sure that the AJAX content is accessible without javascript. You can use a hashbang (#!) like Twitter does to let Google know you’re using AJAX. For more information you could check out this guide by google.

Pushing the state

With HTML5 you can access a history object through which you can navigate based on the url history. You can also pushstate or push a new url to the address bar without the page reloading. This url is added to the history thus can be manipulated later on.

Straight from the MDN:

Suppose http://mozilla.org/foo.html executes the following JavaScript:

var stateObj = { foo: "bar" };
  history.pushState(stateObj, "page 2″, "bar.html");

This will cause the URL bar to display http://mozilla.org/bar.html, but won’t cause the browser to load bar.html or even check that bar.html exists.

Note that since this is a brand new HTML5 feature it won’t work everywhere. For example at this moment it won’t work in IE at all.

Using everything together

You could use a combination of those techniques to create a couple of layers to progressively enhance the state mapping:

  • Write normal links to different pages in HTML
    This way people without javascript (and Google depending on wether you use a hashbang) can still use your website.
  • If javascript is supported change all the links to hashlinks. This way you can watch the state using onhashchange.
    • If that’s not supported you can check the hash at an interval (in combination with watching the click).
  • If pushstate is supported push the states to the url instead of the hashes.

You would have to make sure that all your content is available for everybody. And even more work: make sure that people with pushstate can visit hashchange and all other possible combinations.

History.js

Luckily there is History.js, a javascript library that we can feed states and it will worry about what techniques it should use. This way abstracting away all the problems above. It’s also available in jQuery plugin and a bunch of other stuff.