An Overview of the JavaScript History API

Share this article

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="https://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="https://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.

Frequently Asked Questions (FAQs) about JavaScript History API

What is the JavaScript History API?

The JavaScript History API is a powerful tool that allows developers to manipulate a website’s history stack. This API provides methods and properties to manage the user’s session history (the pages visited in the tab or frame that the current page is loaded in). It allows you to load a new document, move forward or backward through the user’s history, and manipulate the contents of the history stack.

How does the pushState() method work in the JavaScript History API?

The pushState() method in the JavaScript History API is used to add a new entry to the history stack. It takes three parameters: a state object, a title (which is currently ignored by most browsers), and a URL. The state object can be anything that can be serialized, and it represents a state that should be restored when the user navigates back to this history entry.

What is the difference between pushState() and replaceState() methods?

Both pushState() and replaceState() methods are used to manipulate the history stack. The main difference between them is that pushState() adds a new entry to the history stack, while replaceState() modifies the current history entry instead of adding a new one. This means that replaceState() will not affect the length of the history stack.

How can I use the popstate event in the JavaScript History API?

The popstate event is fired whenever a history entry is activated, either by the user clicking the back or forward button, or by JavaScript code calling the history.back(), history.forward(), or history.go() methods. You can use the popstate event to update your page’s content based on the current history entry.

Can I use the JavaScript History API with React?

Yes, you can use the JavaScript History API with React. In fact, React Router, a popular routing library for React, uses the History API under the hood to manage navigation and URL in React applications.

Is the JavaScript History API supported by all browsers?

The JavaScript History API is widely supported by all modern browsers, including Chrome, Firefox, Safari, and Edge. However, it is not supported by Internet Explorer 9 and earlier versions.

Can I store complex objects with the pushState() method?

Yes, the pushState() method allows you to store complex objects as the state parameter. However, these objects must be serializable because they are internally converted to a string using the structured clone algorithm.

How can I handle browser compatibility issues with the JavaScript History API?

To handle browser compatibility issues with the JavaScript History API, you can use feature detection to check if the API is available before using it. You can also use a polyfill, which is a piece of code that provides the functionality that you expect the browser to provide natively.

Can I use the JavaScript History API to navigate to a different domain?

No, the JavaScript History API can only be used to navigate within the same domain. It does not allow you to navigate to a different domain due to security reasons.

How can I debug issues with the JavaScript History API?

Debugging issues with the JavaScript History API can be done using the browser’s developer tools. You can inspect the history stack, monitor the popstate events, and step through your code to identify and fix any issues.

Sandeep PandaSandeep Panda
View Author

Sandeep is the Co-Founder of Hashnode. He loves startups and web technologies.

APIsHTML5 Tutorials & ArticlesIntermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week