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:
- Collect all the tab panels using
$('div.tabs > div')and initialise to show the first tab1 - Listen for clicks on the links that form the actual tabs
- 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
- 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.
You should follow me on Twitter here I tweet about jQuery amongst the usual tweet-splurges!
Related screencasts
Demo
If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.
Source Code

Play QuickTime version
Play Flash version

Rajeev On 12th May 2010 at 09:05
I am very fond of your posts as you give some good tips and tricks to reach the solution. This is the one i need now. The code works for me, i have tried it and it runs successfully. Thanks for sharing the code snippet. Hoping for some new stuff in future..
Jon Wyatt On 17th May 2010 at 08:05
great tutorial thanks.. was impressed with your confidence in doing it ‘blind’ then when you went down a cul-de-sac with pushstate and reversed out it was really effective learning for the viewer as well.. made me realise that most pre-prepared tutorials are actually editing out a lot of the learning as well.
will definitely check out BBQ esp for controlling multiple widgets
re your video player, when pausing to study code samples it showed the embed code overlay, bit annoying cos you (I) instinctively want to pause and study (i know code was available separately but still)
great stuff thanks! Jon
Joris de Beer On 19th May 2010 at 03:05
Hey, great demo, really enjoyed the over the shoulder experience of watching you hack this together, rather than someone running through it very prepared. It’s helpful to watch how others code and debug, and work things out for the first time. Thanks.
Alexandr On 19th May 2010 at 07:05
Thanks! Passed it on to fellow developers. Good job.
Perth Web Designer On 20th May 2010 at 01:05
I’ve only know and use click events on links as triggers in the past, so now I know of another method. Thanks for sharing.
Ryan On 20th May 2010 at 08:05
“hashchange” plugin works better than “history” plugin
Florian On 31st May 2010 at 13:05
Hi,
is there a chance to get this worked together with ui.tabs? Right now if i activate ui.tabs there is no function on the tabs anymore :(
thx Florian
Joe On 1st June 2010 at 12:06
Great, this is what I’ve been looking for!
anonymous On 13th June 2010 at 04:06
Would love to see a tutorial on integrating malsup’s jquery form plugin and the bbq plugin where radio buttons or checkboxes, not anchors, are the click events.
Tony Accardo On 1st July 2010 at 18:07
I had once made a web browser using MS Visual Studio. The way you lucidly explain each step, I think I’ll actually start learning doing the coding from scratch just for fun! Thanks! I really like your style of explanation.
Eli On 2nd August 2010 at 08:08
How would you apply this to use ajax to load pages in?