Maintaining Browser State in Ajax Apps

Using URL Hashes to Maintain Page State

A common problem with Single-Page Applications (SPA) running in web browsers is how to maintain state. Since the URL of an SPA doesn’t change by design there is no way to bookmark a particular view, and the browser’s ‘back’ button no longer works.

Read on if you need to have a working ‘back’ button and support for deep-bookmarking

Hashing the URL

One efficient way to solve this is to add a hash value to the URL. This emulates the look and feel of a GET request, but will work with both GET and POST.

To do this we only add the hash after the link has been clicked. This means we need to intercept the click, add the hash to the url, and then perform the action the click requires.

This requires a different kind of link in the HTML to replace the familiar HREF. For example, a menu of choices might include a link to read an article. We construct the link this way:

<A onClick=\"menu('articleView', 1)\" onMouseOver=\"'pointer'\">Read Article</a>

This calls the following Javascript function to change the URL by adding a hash ‘#articleView?1’.

function menu(pageName, pageNum){
        var hash = window.location.hash.substring(1);
        hash = pageName + "?" + pageNum;
        location.hash = '#' + hash;
}; becomes

We also have the following function:

$(window).bind('hashchange', function () {
	var hash = window.location.hash.substring(1); 
	var params = hash.split("?");
	getPage(params[0], params[1]);

This is called when the hash is changed. It splits the hash back into it’s original parameters and passes them to the ‘getPage’ method, which is responsible for calling the back-end and sending the results to the browser for display.

Thus every view into the app has a unique URL that can be bookmarked and navigated with the back and forward buttons.


This is a simple example that can be readily used for menus and pagination, but it can easily be extended depending on how you choose to encode parameters into the hash.

Further advantages of this method are that it concentrates calls to the back-end into a single function minimizing the amount of JS processing on the client side, and limiting the entry-points into the back-end.