An Overview of the JavaScript History API

Sandeep Panda

Modern web applications can access a user’s browsing history using the History API. As of HTML5 you can also manipulate history entries with great flexibility. This tutorial gives an overview of JavaScript’s History API, and explains how to use this feature while designing modern web applications.

Controlling History

The History API allows developers to add, remove, and replace history entries, altering the behavior of the Back and Forward buttons. Additionally, you can extract state information and use it to manipulate the content of a document. All of this is done using the history object – a property of window.

Moving Backward and Forward

The history object offers two useful methods for cycling through user history, back() and forward(). Calling history.back() will take the user one step back in the browser’s history. This has same effect as hitting the Back button. Similarly, calling history.forward() has the same effect as pressing the browser’s Forward button.

Moving to a Specific History Point

The history object provides another method, go(), which takes the user to a specific history point. For example, if you call history.go(-3) it will take the user back three pages. Similarly, calling history.go(3) will take the user three pages forward. Calling history.go(-1) and history.go(1) have the same effects as calling history.back() and history.forward(), respectively.

Note: IE allows developers to pass URLs to go(). However, this is not standard, and should be avoided.

Counting the Number of History Entries

The number of pages in history can be found by accessing the history object’s length property, as shown below.

alert(history.length);

Manipulating History Entries

The history object provides two methods, pushState() and replaceState(), for adding and replacing history entries.

Using pushState()

Let’s say the following code is executed on http://localhost/index.html:

history.pushState({page:2},"Page 2","page2.html");

This will cause the browser to change the URL of the current page to http://localhost/page2.html. But, it will not change the content of the page or reload it. The browser won’t even check that page2.html exists. It will simply display the URL in the address bar.

Now, suppose you visit http://www.sitepoint.com and hit the Back button. The browser will load http://localhost/page2.html as it was added to the history stack previously. As the page is loaded, it will also receive a popstate event. In the above JavaScript code we passed an object to pushState() as the first argument (this is known as state object). We can retrieve this object from the popstate event’s state property and use it to manipulate the content of the document.

The pushState() method takes the following three parameters:

  1. State Object – This object is associated with the new history entry that’s being added to the stack.
  2. Title – The title of the new history entry. Currently Firefox ignores this parameter, but it may be used in future.
  3. URL – The URL to display to the user. It can be absolute or relative, but the URL should be of same origin as the current URL. Otherwise, the method will throw an exception.

To reinforce the concept of pushing states, assume we have three pages: index.html, page1.html, and page2.html. The HTML content of index.html is shown below.

<html>
  <head>
    <title>Demo Page</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
  </head>
  <body>
    <input type="button" id="push" value="Push History"/>
  </body>
</html>

Now the following JavaScript code is added to the document:

$(document).ready(function(){
  $("#push").click(function(){
    history.pushState({page: 1},"Page 1","page1.html");
    history.pushState({page: 2},"Page 2","page2.html");
  });
});

The following JavaScript code is added to page2.html:

$(document).ready(function(){
  window.onpopstate=function(event){
  alert("location: "+document.location+" and state: "+event.state.page);
  }
});

If you load http://localhost/index.html and click the Push History button, it will add two history entries and cause the browser to display the URL http://localhost/page2.html. However, the content of page will not change. If you navigate to some other page and then press the Back button, the browser will load page2.html. The page will also receive a popstate event. The state property of this event will contain a copy of the state object used while adding the history entry with history.pushState().

If you again press the Back button again, the URL will change to http://localhost/page1.html and another popstate event will be received by page2.html. Note that although the URL is changed to page1.html, the content of page2.html is displayed.

Using replaceState()

history.replaceState() acts in the same way as history.pushState(), but it modifies the current history entry instead of adding a new one.

Practical Usage

When you click on a specific photo in a Facebook album you can see that the URL changes and a picture is displayed. All of this happens without reloading the page.

Also check out the Chrome Web Store. When you click on a specific app, all the details of the app are shown in a modal window, and the URL of the page changes. The point is that each app has its own URL that users can bookmark.

We can implement a similar functionality using the History API. In this example, we will create a simple photo gallery. If the user clicks on a specific photo, the picture opens up in a light box style. We enhance the functionality by giving each photo its own URL. When a picture opens, the URL of the page is changed to reflect the currently opened picture’s address.

Step 1

We start by creating a simple HTML page and adding a few images to it, as shown below. Note that I have included prettyPhoto, a jQuery plugin for creating a lightbox effect.

demo.html

<html>
  <head>
    <title>Demo Page</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script src="js/jquery.prettyPhoto.js" charset="utf-8"></script>
    <link rel="stylesheet" href="css/prettyPhoto.css"/>
    <script type="text/javascript" src="customurl.js" charset="utf-8"></script>
  </head>
  <body>
    <div align="center">
      <a href="/demos/history/pics/image1.jpg" rel="prettyPhoto" id="1"><img src="pics/image1.jpg" height="300"
      width="300"/></a>
      <a href="/demos/history/pics/image2.jpg" rel="prettyPhoto" id="2"><img src="pics/image2.jpg" height="300"
      width="300"/></a>
      <a href="/demos/history/pics/image3.jpg" rel="prettyPhoto" id="3"><img src="pics/image3.jpg" height="300"
      width="300"/></a>
      <a href="/demos/history/pics/image4.jpg" rel="prettyPhoto" id="4"><img src="pics/image4.jpg" height="300"
      width="300"/></a>
    </div>
  </body>
</html>

Step 2

We proceed to add some JavaScript to the page. The content of customurl.js, which is included in the page, is shown below. First, we create a photo gallery by initializing prettyPhoto. Next, when the user clicks on a link, we grab the image number and create a fake image URL based on the number. Then we push it onto the history stack. Similarly, when the user closes an image we pop the current history entry from the stack. As a result the original URL comes back to address bar.

$(document).ready(function(){
  $("a[rel^='prettyPhoto']").prettyPhoto({
    callback: function(){history.pushState("","","/demos/history/demo.html"); document.title="Demo Page";}
  });
  $("a").click(function(){
    var id=$(this).attr("id");
    var img="image"+id;
    var url="images/"+img+"/";
    history.pushState("","",url);
    document.title=img;
  });
  function getParameter(name){
    if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search))
      return decodeURIComponent(name[1]);
  }
  var image=getParameter("id");
  if(typeof image !='undefined'){
    var event=document.createEvent('MouseEvents');
    event.initEvent('click',true,true);
    document.getElementById(image).dispatchEvent(event);
  }
});

Step 3

What if a user directly accesses the fake image URL? The browser will send a 404 error indicating that the page was not found. To overcome this, we create a server side PHP script. The script gets the image number from the request and redirects the user to demo.html, appending the image number as a query string parameter.

In our JavaScript code inside demo.html, we have defined the getParameter() function which searches the URL to find the image number. We have already given our photo gallery links specific id attributes which represent the image number. If the image number is found from the URL, we create a click event programmatically on the particular link. As a result, the image is displayed with lightbox effect.

You can find a complete working demo here.

Further Reading

You can visit the Mozilla Developer Network to learn more about the History API. Also, check out the history object to learn more about its properties and methods.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Pavel

    Html code snippets are ugly, you should fix it.

    • http://www.cjihrig.com Colin Ihrig

      Thanks Pavel. This seems to be an ongoing issue. Our devs are looking into it. In the meantime, I’ve corrected the snippets.