Search

Enabling the Back Button

Posted on 19th April 2010 — As we build more and more Ajaxy applications, and our apps reside on a single page, the browser’s native back button can get more and more broken. This episode will show you how to re-enable the back button on your apps.

Watch

Watch Enabling the Back Button screencast (Alternative flash version)

QuickTime version is approximately 50Mb, flash version is streaming.

View the demo used in the screencast

The Problem

Using our original jQuery tabs solution, we have a tabbing system that you can click the tabs and different content loads. However, when I hit the back or forward buttons, the web page navigates completely away from the site.

We want to fix this, so that I can navigate the tabs using the browsers native back and forward buttons.

“Cowboy” Ben Alman’s BBQ

Ben wrote a jQuery plugin called BBQ. I knew this was supposed to add support for the back button (BBQ standing for Back Button & Query), so I would rework the existing tab demo to make use of this plugin.

Now, in retrospect, I think that I only need Ben’s hashchange plugin as that’s all I ended up using in the screencast, but none the less, they’re both worth checking out.

Now armed with Ben’s plugin, we’re going to refactor the tab code so that the back button works.

Solution

The way the existing tabs work is as follows:

  1. Collect all the tab panels using $('div.tabs > div') and initialise to show the first tab1
  2. Listen for clicks on the links that form the actual tabs
  3. When a tab is clicked, hide all the tab panels, filter down to the one we wanted to see and show it, then update the classes on the tabs so the current link appears to be focused
  4. Finally, initialise by finding the first tab and triggering a click

#1. Actually, you don’t need the subsequent lines after the hide: .filter(':first').show() as the trigger hashchange will handle that for us, but I’ve left it in place so it matches the screencast.

This process was our original code, and most of it needs to stay in place. The change in approach is this: instead of listening for clicks on the tabs, we listen for when the URL in the browser changes.

So we listen for the hashchange event, just like we might listen for a click event:

$(window).bind('hashchange', function () {
  // do some magic
});

Now we move all of the original code from the click handler in to this new event listener. I’ve copied this code in, and marked in bold which lines we’ll need to change:

$(window).bind('hashchange', function () {
  tabContainers.hide();
  tabContainers.filter(this.hash).show();
  $('div.tabs ul.tabNavigation a').removeClass('selected');
  $(this).addClass('selected');
  return false;
});

At this point all the references to this need to change, because in our original version this referred to the link that had be clicked. Now we need to determine the link based on the window URL. We can get the newly navigated URL using window.location. This is an object that represents part of the URL, and as such, gives as just the hash by itself: window.location.hash. This is good.

So this is used twice in the code above, once to find the tab panel that we want to show using this.hash. This first one is easy to change. Since the link has been clicked, the address has changed to match the hash of that link, i.e. we clicked on <a href="#one">first</a> so the URL is bbq.html#one. Instead of using this.hash we can use the window.location.hash instead – they’ll match exactly.

Next we need to address how we can target the appropriate tab to add the selected class. Since we don’t have this to target the right tab, we need to find the element using jQuery. Since we have the hash, we can use this to find the tab link. We can use the “hash” attribute selector like this:

$('a[hash=#first]')

Therefore we can substitute our hash variable in to the ‘#first’ part of the selector above, and now we’ll have the right element to add the selected class.

Finally in this code, we just have to remove the return false as it’s not required at all.

Then one last change to make to our code – we need to trigger the hashchange event, which will cause our code to run.

jQuery

Our final code looks like this:

$(function () {
  var tabContainers = $('div.tabs > div');
  tabContainers.hide().filter(':first').show();

  $(window).bind('hashchange', function () {
    var hash = window.location.hash || '#first';

    tabContainers.hide();
    tabContainers.filter(hash).show();
    $('div.tabs ul.tabNavigation a').removeClass('selected');
    $('a[hash=' + hash + ']').addClass('selected');
  });

  $(window).trigger("hashchange");
});

Once last note: if we trigger the hashchange event, and there’s no hash on the URL, then we need to give it a default. In this case I’ve given it #first as a default so that it always lands on the first tab if there’s nothing pre-selected.