The HTML5 History API
Thursday, January 20, 2011
This post is a continuation of my previous post “AJAX Broke The Web”. I recommend you read this first, to understand the scope of the problem we are trying to solve.
The HTML5 History API has been introduced as part of the new HTML5 Specification. It introduces 2 new methods to the window.history object, pushState and replaceState, and a new event to the window, onpopstate.
pushState
In essence, pushState adds a new state to the history stack.
// This object is passed to the onpopstate event when it is fired. You
// can fill this object with what ever you like.
var stateObject = {
title: 'This Awesome Post',
createdAt: '2010-10-10',
author: 'keithpitt'
};
// At the moment, most browsers are ignoring this property, so just fill it with
// the title of the new page. According to Firefox, they _may_ use it in the
// future.
var title = 'This Awesome Post';
// The full URL of the new page.
var url = '/posts/this-awesome-post';
history.pushState(stateObject, title, url);
replaceState
replaceState acts exactly the same as pushState, but it doesn’t add an entry to the history stack.
I cannot think of a good example of its usage in a real world situation, but I did see someone do a cool little URL animation by using it. It can be found on a blog post by Murray Picton.
A Practical Example
I have created an example on how to use the new HTML5 API using Ruby on Rails, and will_paginate. You can play with it at http://ajax-pagination-html5.heroku.com/
I have also provided the source code here: http://github.com/keithpitt/ajax-pagination-html5
It is a fairly straight forward implementation.
I first start by creating a function that I will use when it is time to update the content on the page using AJAX:
function update(url) {
$('#loading').slideToggle('fast', function() {
$.ajax({
url: url,
success: function(html) {
$('#loading').slideToggle('fast');
$('#content').html(html);
}
})
});
}
I then create some event handlers that will push a new state to the history when a user submits the search form, or clicks on a pagination link.
$('.pagination a').live('click', function() {
var href = $(this).attr('href');
history.pushState({ path: href }, '', href);
update(href);
return false;
});
$('form').live('submit', function() {
var form = $(this);
var url = form.attr('action') + '?' + form.serialize();
history.pushState({ path: url }, '', url);
update(url);
return false;
});
We also need to handle the onpopstate event. onpopstate is called when the page is loaded, so we need to check if we have access to the state object. If we do, then that means this history item was added via pushState.
$(window).bind('popstate', function(event) {
var state = event.originalEvent.state;
if(state)
update(state.path)
})
The last peice of the puzzle, is to tweak how we respond to the request in our Rails controller.
class SearchController < ApplicationController
def search
@query = params[:q]
@results = Game.search(@query).paginate :page => params[:page], :per_page => 10
render :layout => false if request.xhr?
end
end
And thats it!
Considerations
The example I provide here will only work in browsers that support the new HTML5 History API. At the time of writing, the only browser is Google Chrome. Firefox 4 will have support for this new API.
My next blog post will introduce a jQuery plugin that will simplify this process, and allow it to function cross-browser.

