Fixing the Back Button: A Simple SPA Behavior using Location Hash


Using a SPA framework, like AngularJS, is overkill for a simple one-page website. But, suppose that single web page has pseudo-navigation needs, like a dialog that opens in response to the user clicking a button. This represents a state transition, and clicking the back button in the browser (or on an Android or Windows Phone) should serve to close the dialog, not navigate to the previous page in the browser’s history stack. But, unfortunately, that’s not how web browsers behave.

SPA frameworks make use of the Location Hash as a way to maintain state transitions without actually leaving the current page. Each state is associated with a hash string that gets appended to the page’s URL, and hash changes add a new entry into the browser history stack.

For example, suppose our simple web page has a URL:


Adding a hash at the end of the URL is one way to represent that the dialog box is visible:


When a user presses the Back button from the hash URL, the browser goes to the previous entry in the history stack, which would be the page without the hash.

Now, all that is needed is a simple implementation!

Implementation using Location Hash

First, the HTML for this example:


Notice that the dialog itself includes a “Close” button. When I originally wrote this user experience, I had intended for the Close button to be the only way that the user could hide the dialog. But, then as I tested, I found that I was naturally hitting the Back button to close the dialog on my phone, but that would take me completely out of the page. So, the technique described in this post was the result of a user experience tweak.

Next, we need an event handler for when the user clicks the “Show” button. This will just set the window.location.hash (we’ll see how to actually respond to hash changes in a little bit). As described above, setting the location hash adds an entry to the history stack (as if the user had navigated to a different web page):


When the user clicks the “Close” button inside the dialog, we’re just going to pop the history stack. This means that the browser’s Back button and the Close button have identical behavior.


And finally, we need to handle the Hash Change event on the window object. In this case, if the “#dialog” hash is present, then we set the dialog to display and hide the Show button. If it is not present, then we hide the dialog and display the Show button. Note that this event does not fire when the page is first loaded.


Something to note here is that even though we set the window.location.hash without the hash sign (#), the value when it is read will have a hash prefix.

This example is intentionally very vanilla to show off the capabilities of the browser itself, not a library’s abstraction of these features. Having said that, as your requirements become more advanced, it may make sense to look into some of the libraries that help to smooth out the implementation, such as History.js, Hash.js, #Hasher, PathJS, and the HashChange plugin for JQuery.