Restoring conventional page navigation to your javascript application with jQuery history plugin

I’m not sure how, but my jQuery application suddenly got very complicated. One minute I was adding cool onmouseover effects and then BANG I’m inserting new elements all over the place and I have to look into heavy compression just to get the thing to download this side of the apocalypse.

Browsing through my application is a breeze. All the links are rewired to AJAX requests with smooth transitions and lots of slideUp-and-downs. There’s one glaring hole that might just bring this happy user experience down quicker than a Lehman brother wearing Iron Boots.

Whenever my lovely users click ‘back’ on their browser they get taken back to the obscenity that is the non-standards-compliant flashing gif-ridden site they came from, not the last thing they were viewing on my site. And if he or she clicks ‘refresh’ the application state goes back to the original and my user has to navigate back to the page he or she was viewing all over again. What a knightmare.

The browser navigation controls – back, forwards and refresh – are a staple of any user’s experience. Of course, being a web developer you will recognise the lack of page refreshes and the changing of page elements and automatically look for site-specific navigation links on the page itself. However, your average user is going to get very confused, thinking their browser buttons are broken or worse – your website is broken.

And it’s not only the user that’s going to encounter problems. If a Google Spider can’t see pages with unique URLs it’s not going to index them. So all I’m going to get on a Google search is whatever is on the landing page, which is just header information and a footer – not very useful. If my user wants to bookmark a page for later, he or she is going to have to start again, before all the AJAX fun began. The list goes on. It’s like building a vast palace with turrets and different wings but neglecting to build any doors. Your guests are going to walk over the drawbridge and marvel at the splendour of your architecture, the complete set of Power Ranger themed bedrooms, yet have no idea how to get there.

Anyway, here’s how I solved it:

Step 1: jQuery history plugin

First of all, I downloaded the Mikage’s jQuery history plugin. This is a bit of code that allows me to write to my browser’s history using a hash key. This hash can be pretty much anything that can uniquely describe your application’s state. I chose to make it the same as the resource I was loading into the main part of my application. So the plugin would add ‘www.mysite.com/page1#page2′ to my browser history when I clicked on the ‘Page 2′ link. Once I’d saved the plugin to my computer, I put this line in my head section of page1.html.


<script src='http://www.mysite.com/js/jquery.history.js' type='text/javascript'></script>

Now that jQuery knows I want to use this plugin, I need to initialise before I can start using it. I put this line just below the previous line, also in the $(document.ready()) section.

$(document).ready(function() {

$.historyInit(ajaxRender, 'http://www.mysite.com/page1');
};

Right, now it’s ready to use.

Step 2: The click event

Thinking about it, the page state always changes directly after the user clicks on a link (as it would on any other site I guess). So each of these state changes is tied to an <a> element. Therefore, I’m going to use jQuery’s live() function to tie a function to each link element’s ‘click’ event. So every time a user clicks a link some code will get executed. I used the live() function instead of the more common click() event handler because live() ties all future instances of an element to the function instead of just the ones initially in the DOM. This means I can happily insert new ‘a’ elements knowing that they’ll still respond in the same way. Here’s the click event code (this should also go in the $(document).ready so that it’s ready when the page loads):


$('a.internal').live('click',function() {
var post_url = $(this).attr('href');
//alert(post_url);
$.historyLoad(post_url);
return false;
});

You can see that I’m binding this function to the click events for each ‘a.internal’ element. I’ve given each internal link this class so that I can differentiate between internal and external links. If all links were treated the same, I’d get google.com loading inside my homepage. I extract the ‘href’ part of the link so that I know where the link is pointing. This is then assigned to the variable ‘post_url’. Then I call the historyLoad() method. This tells the jQuery plugin I just set up to add a new page to the browser history. It does this by adding a hash key to the end of the url. For example www.mysite.com/page1 would become www.mysite.com/page1#page2 in the address bar, and my application would then go about rendering the page2 on my page to reflect the change. The ‘return false;’ line is important, as this stops the browser executing it’s normal event handling for the element. If I didn’t include this line, all would be lost, as the browser would merrily redirect us to page2.html (thinking it was a regular link) and page1 would be lost to the wind.

Step 3: Rendering the page

Once historyLoad() has done it’s job and added a page to the browser history, it will then follow a callback function that we defined earlier with historyInit(). The callback function renderPage() is going to run the AJAX request and insert the new data into my page. The code for this is as follows (this can go anywhere in your javascript code):


function ajaxRender(post_url) {
$('body').css('cursor','wait'); // changes the cursor to a wait symbol
if($.browser.msie) { // if the browser is Internet Explorer
// jquery's $.load() function does't work when hash include special characters like åäö.
post_url = encodeURIComponent(post_url); // removes special characters
}
if (post_url) { // checks that a URL is present
$.ajax({ // start the AJAX request
type: 'POST', // I'm sending it via POST, you can use GET if you're feeling dangerous
url: post_url, // the URL we are retrieving
data: null, // in this case we are not sending any data
success: function(data) { // execute the below if the AJAX goes according to plan
$('#page_content').html(data); // put the data retrieved from the AJAX request into the div container
$('body').css('cursor','default'); // put the cursor back to normal
},
error: function (XMLHttpRequest, textStatus, errorThrown) { // execute this if it all goes tits up
$('body').css('cursor','default');
alert(textStatus + errorThrown);
return false;
}
});
} else { // if there is no post_url, i.e. it's the home page
$('body').css('cursor','default'); // put the cursor back to normal
}
};

This function executes the AJAX request and puts the data it retrieves into the #page_content div.

Once you’ve got these three components in your code you should be OK.

Note: If you’re just loading stuff from a file and not sending data it would be a better idea to use jQuery’s load() method.

Feel free to comment if there’s a bit you don’t understand or are having trouble with.

Edit: I just discovered that this process is called ‘deep linking’. Sounds like a bit of a serious term, but it is much shorter than ‘the restoration of conventional page navigation to an AJAX application’. Had I known this before I wrote this post I probably would’ve looked into using the jQuery Address deep linking plugin instead. But my method worked fine for me . . .

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>