Tech and Media Labs
This site uses cookies to improve the user experience.




HTML5 History API

Jakob Jenkov
Last update: 2015-06-18

The HTML5 history API gives you access to the browser navigation history via JavaScript. The HTML5 history API is really useful in single page web apps. A single page web app can use the HTML5 history API to make a certain state in the app "bookmarkable". I will get back to how to use the history API to make bookmarkable states in single page apps later.

The History Stack

The browsing history consists of a stack of URLs. Every time the user navigates within the same website, the URL of the new page is placed at the top of the stack. When the user clicks the "back" button, the pointer in the stack is moved to the previous element on the stack. If the user clicks the "forward" button again, the pointer is moved forward to the top-most element on the stack. If the user clicks "back" and then click on a new link, the top-most element on the stack will be overwritten with the new URL.

Here is an example of a history stack:

http://myapp.com/great-new-story.html
http://myapp.com/news.html
http://myapp.com

The last page visited in the above history stack is http://myapp.com/great-new-story.html . If the user clicks the "back" button the pointer into the history stack will be moved back to http://myapp.com/news.html . If the user clicks the "forward" button the history stack pointer will be moved forward to http://myapp.com/great-new-story.html, but if the user clicks on another link instead (on the http://myapp.com/news.html page), then the URL of that link will overwrite http://myapp.com/news.html on the history stack.

It is this history stack the HTML5 history API gives web apps access to.

HTML5 History API Security Restrictions

The HTML5 history API only gives a web page access to the part of the browsing history which lies within the same domain as the web page itself. This restriction in the history API is required for security reasons, so a web page cannot see which other websites a user has visited.

Similarly the HTML5 history API does not allow a web page to push URLs onto the history stack which are outside the same domain as the domain of the web page. This restriction ensures that a web page cannot pretend to have forwarded the user to e.g. Paypal and sniff up their user name / password etc. when the user starts typing it in.

The history Object

You access the browsing history via the history object which is available as a global object in JavaScript (actually, as window.history).

The history object contains the following functions - which comprise the history API:

  • back()
  • forward()
  • go(index)
  • pushState(stateObject, title, url)
  • replaceState(stateObject, title, url)

The back() function moves the browsing history back to the previous URL. Calling back() has the same effect as if the user clicked the browser's "back" button.

The forward() function moves the browsing history forward to the next page in the history. Calling forward() has the same effect as clicking the browser's "forward" button. This is only possible if the back() function has been called, or if the "back" button has been clicked. If the history already points to the latest URL in the browsing history, there is nothing to move forward to.

The go(index) function can move the history either back or forward depending on the index you pass as parameter to the go() function. If you call go() with a negative index (e.g. go(-1)) then the browser moves back in history. If you pass a positive index to the go() function then the browser moves forward in the browsing history (e.g. go(1)). The index indicates how many steps in the history to move either forward or back in the browsing history, e.g. 1,2, -1, -2 etc.

The pushState(stateObject, title, url) function pushes a new URL onto the history stack. The function takes three parameters. The url is the URL to push onto the history stack. The title parameter is mostly ignored by the browsers. The stateObject is an object that will be passed along with the event fired when a new URL is pushed onto the history stack. This stateObject can contain any data you want. It is just a JavaScript object.

The replaceState(stateObject, title, url) function works like the pushState() function except it replaces the current element in the history stack with a new URL. The current element is not necessarily the top element. It is the element currently being pointed to, which can be any element in the stack, if the back(), forward() and go() functions have been called on the history object.

History API Examples

It is time to see some examples of how you use the HTML5 history API.

back() and forward()

Let us first see how you move back and forward in the history using the back() and forward() functions:

history.back();

history.forward();

Remember, the history object is located in the window object so you could also write:

window.history.back();

window.history.forward();

However, since the window object is the default object you can leave it out. I will leave out the window object throughout the rest of this tutorial.

Remember, you cannot move forward in history unless you (or the user) has first moved back in history.

go()

Now let us see how to use the go() function to perform actions similar to the back() and forward() functions. First, here is how you use go() to move one step back in the browsing history:

history.go(-1);

To move two steps back you would pass -2 as parameter to the go() function, like this:

history.go(-2);

Similarly, to move forward in history you would pass positive indexes to the go() function. Here are two examples that move one and two steps forward in history:

history.go(1);

history.go(2);

Of course, if you executed both of these lines you would move a total of 3 steps forward in the browsing history.

pushState()

To push a state onto the history stack you call the pushState() function of the history object. Here is a pushState() example:

var state = {};
var title = "";
var url   = "next-page.html";

history.pushState(state, title, url);

This example pushes a new URL onto the history stack. This will also change the URL in the browser's address field but will not cause the browser to try to load that URL.

replaceState()

The replaceState() function replaces the history element in the history stack that is being pointed to right now. This may not be the top element if the user has moved back in history using the "back" button. Here is a replaceState() example:

var state = {};
var title = "";
var url   = "another-page.html";

history.replaceState(state, title, url);

Replacing the state will also change the URL in the browser's address field but will not make the browser load that URL. The page that replaced the URL remains loaded in the browser.

History Change Events

The HTML5 history API enables a web page to listen for changes in the browser history. The security restrictions apply here too, so a web page will not be notified of history changes that leads to URLs outside of the domain of the web page.

To listen for changes in the browser history you set an onpopstate listener on the window object. Here is a browser history event listener example:

window.onpopstate = function(event) {
    console.log("history changed to: " + document.location.href);
}

The onpopstate event handler function will get called every time the browser history changes within the same page (the browser history that page pushed onto the history stack). The reaction to a history change event could be to extract parameters from the URL and load the corresponding content into the page (e.g. via AJAX).

Note: Only changes caused by either the "back" or "forward" buttons, or the corresponding history navigation functions back(), forward() and go() will cause the onpopstate event listener to get called. Calling the pushState() and replaceState() functions will not cause a history change event to be fired.

Using The History API in Practice

When a new URL is pushed onto the history stack the URL in the browser's address field will change to the new URL. However, the browser does not try to load that URL. The URL is just displayed and pushed onto the stack as if the browser had visited that page, but the page that pushed the new state stays loaded in the browser.

Pushing a new URL onto the history stack is a useful way to make a certain state in a single page app (SPA) bookmarkable. For instance in a single page online shop the URL of the app may be:

http://myshop.com

This app may be able to show products to the user within the same page, but how does a user then send a link to a specific product to a friend?

The solution is that the single page app pushes a new URL onto the history stack when a new product is loaded. This does not cause the new URL to be loaded, but it does make the new URL be visible in the browser's address field. From here it can be bookmarked or copy-pasted into an email etc. Here is an example of how such a bookmarkable URL could look:

http://myshop.com?productId=234

Or, perhaps a more readable URL:

http://myshop.com/products/234

Or a slightly more REST-ful version (mentioning the type of product too):

http://myshop.com/products/books/234

After pushing this URL to the browsing history the web shop page would load the corresponding product via AJAX and display it to the user.

If the user clicks the "back" button the onpopstate event handler will get called. The web page should then see what the new URL is, and load the product corresponding to that URL, or the frontpage of the app, if the URL returns to http://myshop.com .

Here is a HTML5 code example illustrating the principle of loading data into the browser with AJAX:

<a href="javascript:push('http://myshop.com/books/123');">
    Book 123
</a> <br/>
<a href="javascript:push('http://myshop.com/apps/456');">
    App 456
</a>

<script>
function loadUrl(url) {
    console.log("loading data from url: " + url);
}

function push(url) {
    history.pushState(null, null, url);
    loadUrl(url);
}

window.onpopstate = function(event) {
    console.log("history changed to: " + document.location.href);
    loadUrl(document.location.href);
}
</script>    

This example contains two links with JavaScript click listeners on. When one of the links is clicked the corresponding URL is pushed onto the history stack, and then loaded into the browser.

The example also contains an onpopstate event listener. When the user clicks the "back" or "forward" button this event listener loads whatever URL the browser address field is now showing.

Configuring The Server

The example shown earlier will work if the user clicks on the links and "back" / "forward" buttons. But what if the user sends the URL to a friend or bookmarks it and visits it later?

If the user tries to visit the bookmarked URL http://myshop.com/books/123 then the browser will request that URL from the web server. The web server needs to know that it has to send back the same single page app as sent back from the URL http://myshop.com. You will need to configure your web server to do this.

Similarly, the single page web app has to look at the URL it was loaded with when first loaded, and use that URL to determine what content to load and display. Thus, if the single page app was loaded with the URL myshop.com/books/123 the app should load the corresponding product and display it. This URL check has to take place during the initialization of the single page app.

Browser Support For The HTML5 History API

At the time of writing the HTML5 history API is supported in all modern browsers (IE, Safari, Chrome, Firefox) except in Opera Mini.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC