How to detect when HTML5's history.pushState() is called?

06 Jan 2011

That was the question I had and galambalazs helped me to solve this question on stackoverflow.com.


Problem description and background:

The latest version of my Firefox add-on (FloatNotes), also listens to the event hashchange, which is raised when the fragment-identifier of the URL is changed. As you may know, this technique is used in Ajax-enabled websites (like Google Mail) to make URLs be part of the browsing history without reloading the page. So the idea is that if the hash has changed, the user actually visits another page.

Now, with HTML5, the window.history object gets new methods which take the previous mentioned idea further: pushState and replaceState. As the name suggests, pushState adds a new URL to existing history stack and (if the protocol and host stay the same) does not trigger a reload of the page. Most browsers will display the new URL in the address bar too so it really looks like you clicked a normal link, but the content is loaded via Ajax. You can test this using Firefox 4 or Chrome 8 and Facebook.

The problem is that calling pushState does generate any event (like changing the hash did) so loading of new content cannot be detected anymore.


The solution

I first tried to replace the whole history object with a custom object, but that did not work (probably window.history is readonly). Then galambalazs proposed to just replace the pushState method. Here is his code:

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        // ... whatever else you want to do
        // maybe call onhashchange e.handler
        return pushState.apply(history, arguments);
    }
})(window.history);

So whenever the pushState method is called, you can trigger your own event handler.

It is a hack, but until an event is raised by default, this seems to be the only way to get notified when history.pushState() is called.