Manipulating Browser's History With HTML5 History API

Background

Browser provided ways for user to navigate around the web by using something call a URL(Uniform Resource Locator) either by clicking on the link or entering it directly into the browser's location bar. Browser making a request to the server identify by the URL and the server perform some work and send back the webpage to the browser to render resulting in what we call the full page reload. That is how the traditional web works over the year.

Some webpage almost look exactly the same different only in some little change in content, so it seem to be wasteful to request for new page that will cause the full page reload while what we want to do is to only change the part that need to change.

Given this situation that web developer came up with the idea of using others technologies such as AJAX to fetch only the content that need to change combine with some DOM manipulation using jQuery or some other javascript framework to replace new data with old one and display it on the webpage this resulting in faster page load.

But there are limitation with this techique. Suppose the user go to the page and interact with the element on the page that involve using above technique then he/she suddenly want to go back one step by pressing the back button on browser then BOOM! That is where HTML5 History API come in.

What is HTML5 History API?

It is a set of javascript API that provided by the browser in the form of a history property object that attach to window object. It lets us manipulate the browser history so that when the user make some change and click the back button but still take back to where he/she was from.
Bellow are the list of public API that history object provide:

The history.pushState() method

This method take three arguments

  • The state object is a JavaScript object which is associated with the new history entry created by pushState(). Whenever the user navigates to the new state, a popstate event is fired, and the state property of the event contains a copy of the history entry's state object.
  • title Firefox currently ignores this parameter, although it may use it in the future. Passing the empty string here should be safe against future changes to the method. Alternatively, you could pass a short title for the state to which you're moving.
  • URL The new history entry's URL is given by this parameter. Note that the browser won't attempt to load this URL after a call to pushState(), but it might attempt to load the URL later, for instance after the user restarts the browser. The new URL does not need to be absolute; if it's relative, it's resolved relative to the current URL. The new URL must be of the same origin as the current URL; otherwise, pushState() will throw an exception. This parameter is optional; if it isn't specified, it's set to the document's current URL.

The history.replaceState() method

This method operates exactly like history.pushState() except that replaceState() modifies the current history entry instead of creating a new one.

The popstate event

A popstate event is dispatched to the window every time the active history entry changes. If the history entry being activated was created by a call to pushState or affected by a call to replaceState, the popstate event's state property contains a copy of the history entry's state object.

Time to practice

The demo can be found here.
First lets setup some basic html structure along with some css. It is pretty basic so no need to explain.

    <h1>Navigation Without Refresh <span></span></h1>
    <h2>Current Page: <span>/</span></h2>
    <ul id="menu-nav">
        <li><a href="/page-1/" title="Pagina 1">Page 1</a></li>
        <li><a href="/page-2/" title="Pagina 2">Page 2</a></li>
        <li><a href="/page-3/" title="Pagina 3">Page 3</a></li>
    </ul>
    body {margin:10px}
    h1 {font-size:20px}

Then next is the actual javascript code that manipulate the browser's history

    var setCurrentPage = function(url) {
        $('h2 span').html(url || "/");
        $("#menu-nav a[href='" + url + "']").fadeTo(500, 0.3);
    };

    $('#menu-nav a').click(function(e){
        e.preventDefault();
        var targetUrl = $(this).attr('href'),
            targetTitle = $(this).attr('title');

        $("#menu-nav a[href='" + window.location.pathname + "']").fadeTo(500, 1.0);

        window.history.pushState({url: "" + targetUrl + ""}, targetTitle, targetUrl);
        setCurrentPage(targetUrl);
    });

    window.onpopstate = function(e) {
        $("#menu-nav a").fadeTo('fast', 1.0);
        setCurrentPage(e.state ? e.state.url : null);
    };

OK, now let first take a look at setCurrentPage function. All it does is change the content of span element that is a child of h2 element to whatever the url that passed in and add the fadein animation effect to make it look nice nothing fancy.

Next is we bind the click event handler to all link elements contain within div. This event handler does three important things first it prevent the default link behaviour to stop it form making the actual request to the server in order to prevent page reload, second is the use of window.history.pushState to add new entry into browser's history with the state object that has one property name url to the current clicked link's href attribute value, title to the current clicked link's title attribute value and the final url argument to the value of href to make the browser's location bar change to this url and the thrid is make the change to the DOM element by using the above setCurrentPage function that we've just discussed. We also add fadeout animation effect to the link element whose href value contain the last url in browser' location bar before we change it using history.pushState().
Now whenever we click on the link it will show the value of href attribute that we clicked in the span that is the child of h2 and also change the browser's address bar to make the user think that we actually making the request for new page load.
So to complete the picture of tricking the user into thinking that he/she actually make pull page load we need to deal with the problem when user click on the back button. That is what to final pices of code does.

The final pices of code is the used of window popstate event. As you can see in the discussion above about when this event is fire so all this code does is to attach event handler to this event and restore to the old state. We can access to the state object that we pass in when we use pushState as a property of event object and call to the setCurrentPage function to display it back on page.

Wrap up

Although HTML5 come with a set of powerful History manipulation API it has some limitation. It only support in only the newer version of browser that you can find a list here.
But the good news is that there are some people out there who have write some wrapper library to take advantage of this new functionaly but still make it support older browser, so for further reading about it you can check it out here