Overview

Enabling the back button isn’t quite as difficult as it might sound, especially for a project that your starting from scratch. Here’s a simple example and then we’ll go through the techniques needed to make this happen.

Change Background Color To

If this is not working for you, then you can try this test page.

As you click the different buttons, you should notice the URL of this page is changing. The key is this portion below.

#change-background:990000

When we change the hash of the page, we aren’t requesting a new page from the server, the browser thinks we are navigating to a new portion of the page. (Some people know that if there was an ID of change-background:990000 on the page, the browser would attempt to focus on that element) Because of this, it creates a history entry that we can use to track back button usage, and deep linking.

Note on browser inconsistencies

Now obviously not all browsers handle this the same way, thus we use a library which abstracts all these problems away from you and lets you only worry about your application. The library we are using here is RSH (Really Simple History). Here are a few other libraries you could use.

Code

Lets look at the code necessary to implement this feature.

  1. <h4>Change Background Color To</h4>
  2. <ul class="button-list">
  3.     <li><a class="post-internal" rel="465e77" href="/denim/index.php">Denim</a></li>
  4.     <li><a class="post-internal" rel="990000" href="/maroon/index.php">Maroon</a></li>
  5.     <li><a class="post-internal" rel="000000" href="/black/index.php">Black</a></li>
  6. </ul>

This is the HTML for the 3 buttons, some of the important things to note here.

  • I tagged each button with a “post-internal” class. This allows me to create a generic function that attaches the history logic.
  • I attached the history information to the element. I typically use the rel to attach information to elements, I don’t know why, its just the way its always been done.
  • I’ve added an href, so if the user doesn’t have Javascript enabled, they could navigate to the separate index pages in the color directories which would know they need to color the page. This is certainly overkill for a background color change, but its one of the important aspects of unobtrusive Javascript.

Obviously the heavy lifting is in the Javascript.

  1. window.dhtmlHistory.create({
  2.         toJSON: JSON.encode,
  3.         fromJSON: JSON.decode
  4. });
  5.  
  6. window.addEvent("load", function() {
  7.     window.dhtmlHistory.initialize();
  8.     window.dhtmlHistory.addListener($listener);
  9.    
  10.     $$(".post-internal").addEvent("click", function(ev) {
  11.    
  12.     window.dhtmlHistory.add("change-background:" + this.getProperty("rel"));
  13.    
  14.     //--- Event Code
  15.     changeBgColor(this.getProperty("rel"));
  16.    
  17.     //--- Don't go to the HREF address.
  18.     new Event(ev).preventDefault();
  19.     });
  20. });
  21.    
  22. function $listener(newLocation, historyData) {
  23.     //--- If not the url event we are looking for, escape.
  24.     if(newLocation.indexOf("change-background:") == -1) return;
  25.    
  26.     //--- Set the background of our HTML element to the color specified in the URL.
  27.     changeBgColor(newLocation.replace("change-background:", ""));
  28. }
  29.  
  30. function changeBgColor(newBgColor) {
  31.     $(document.body).setStyle("background", "#" + newBgColor);
  32. }

This is the entire contents of my sample script file, so we’ll go down through each section.

  1. window.dhtmlHistory.create({
  2.         toJSON: JSON.encode,
  3.         fromJSON: JSON.decode
  4. });

RSH creates itself in the property window.dhtmlHistory, so thats already going to be there for you. RSH uses some JSON to track its history data, so you’ll need to supply it some methods for serializing and de-serializing. It does come with these methods in a separate file, but since Mootools already includes these, I just used the ones from Moo.

This you can go ahead and do outside of any events.

  1. window.addEvent("load", function() {
  2.     window.dhtmlHistory.initialize();
  3.     window.dhtmlHistory.addListener($listener);
  4.    
  5.     $$(".post-internal").addEvent("click", function(ev) {
  6.    
  7.     window.dhtmlHistory.add("change-background:" + this.getProperty("rel"));
  8.    
  9.     //--- Event Code
  10.     changeBgColor(this.getProperty("rel"));
  11.    
  12.     //--- Don't go to the HREF address.
  13.     new Event(ev).preventDefault();
  14.     });
  15. });

A bunch of stuff here, we attach our initialization logic to the window load event as RSH explicitly states

RSH will break, especially in IE, if you try to initialize it from any flavor of DOMContentLoaded.

Once in the loaded event, we call initialize to start the process, and then we add our listener. This listener function is what will get called every time someone hits the back button, forward button, or even the page being loaded with a hash value.

Inside the onload event, we add our onclick event for the buttons.

When the button is clicked, we use the add() method of RSH to add a new history entry. While the listener method is probably 40% of the logic you will need to provide to implement history tracking, this single call is the other 40%. Really those two features make up the meat of it.

The value we pass to the add needs to be descriptive enough that we can figure out what to do with the information in our listener event. In my example, I’m appending “change-background:” to the hash code, so when my listener fires, I know that it has the information I want (someone else isn’t doing some other history tracking) and that I know what to change the background to.

Now when you add a history entry, RSH does NOT fire the history change event listener. (It really should, more later) So you need to also attach your logic for the event as well, which is a simple changeBgColor method with the rel information of the clicked on link.

Lastly, we don’t want to actually go to the URL specified in the anchor tag so we use preventDefault() to cancel that.

  1. function $listener(newLocation, historyData) {
  2.     //--- If not the url event we are looking for, escape.
  3.     if(newLocation.indexOf("change-background:") == -1) return;
  4.    
  5.     //--- Set the background of our HTML element to the color specified in the URL.
  6.     changeBgColor(newLocation.replace("change-background:", ""));
  7. }

This is our event that gets fired every time the history changes. When our listener here gets fired, it looks to see if any change-background information is in the URL, and then it parses out the hex value and calls the changeBgColor method.

With this method, you’ll want to start thinking of the URL as a full description of the state of your application. The less you track in your URL, the less the user can bookmark to, and the less they can back button out of. The extent to which you implement this is totally up to you, and admittedly this could make your URL quite complex…

Gripes about RSH

I wanted to use RSH because its had quite a bit of development on it, so I assumed it would be a solid bug free library. Plus it has one purpose, Ajax applications history tracking, unlike SWFAddress which has some integration with Flash and thus feels like using the wrong tool for the job sometimes.

Though I have a big problem with it not firing the listener event when we use the add() method. Since we put all the information in the URL, and we have to write the listener method to parse that information and set the page to that state, there’s no reason to put that logic anywhere else. Be aware you won’t always have to duplicate your code like this in the other libraries, SWFAddress specifically.

One last note about Deep linking

When you implement the back button, you get deep linking for free. I know, you rarely get things solved for free, but since we are using the URL to describe the state of our application, when the page loads up RSH will fire off our listener event notifying us theres some information in the hash. From there, our application doesn’t care how that information go there, its just going to operate on it.

Download Sample Code

Justise-BackButton.zip

7 comments

7 Responses to “Enabling the Back Button in Ajax Applications”

  1. DotNetShoutout Says:

    Enabling the Back Button in Ajax Applications…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. BEM Says:

    Interesting write up. However, this page throws javascript errors when loaded in IE and it does not function properly. I dislike IE as much as anyone, but since so many folks still use it, supporting IE is a requirement.

  3. Kris Gray Says:

    I totally agree.

    Strangely enough the Recent comments widget that WordPress provides was causing the Script error.

    I’ve removed the widget till I can figure out whats wrong with it.

    Thanks for the note, glad I was able to get that resolved and glad you liked my post.

  4. Kris Gray Says:

    Woops, seems its really Google Analytics fault, probably in some small conjunction with wordpress’s comments template.

  5. Kris Gray Says:

    There we go, this solved it.

    http://wordpress.org/support/topic/224480

  6. Heru Setiawan Says:

    Thanks for the details, Kris.

    I tried using RSH with prototype js but it seems to be working only on FireFox but not on IE6,7 or Safari. I have read their docs but it seems that safari does not work at all.

    I wonder if you can make this code works on Safari Mac OS X. Thanks!

  7. Kris Gray Says:

    Hmm, seems like it should work, unfortunately I don’t have access to Mac OS X otherwise I’d give it a few minutes to see whats up. I am currently writing this comment in Chrome and the example still works, so Webkit seems fine.

Leave a Reply