Introduction

Along with the advent of HTML 5, a new API in JavaScript also sprung up to aid in working with the history of the browser's current session. We call it the History API.

It's packed with a total of five methods — two being so powerful that they're begin used by big tech companies on their respective websites; companies such as Google, Facebook, Twitter, Medium, and the list goes on and on and on.

And let's not forget about the properties of the API — it comes with a toal of three properties, two of which have particular importance.

Let's get to some serious reading...

The history object

The History API is exposed to us via the history property on the global window object.

history holds a History object with properties and methods to aid us in working with history of the current session.

The last part in the previous sentence is very importance to understand. It says that the History API deals with the 'history of the current session'.

But what is the current session?

You open up a browser tab, navigate to page A, read something, then navigate to page B to message your friend, then navigate to page C to learn about JavaScript — all this denotes a session.

The moment you open up a new tab, you're in a new session.

If you go back to the previous tab you were working in, you go back to its respective session. In this way, we can say that a new browser tab denotes a new session.

And coming back to our definition, the History API gives us control over the current session.

This means that if we're on a page A in tab T and work with History object there, we could operate on the history of the session of tab T only, not any other tab.

The History API doesn't magically give the developer control over the entire history of the end user's browser. It would have been security-wise vulnerable, too much memory intensive, and at the end of the day, useless if it did so.

So with this distinction in mind as to what the API actually gives us to work with, let's see each of its properties and methods one-by-one.

Properties

length

The length property returns the total number of entries in the current session's history stack.

When you open a new tab, and have visited nothing in it as of yet, history.length would be 1 since that initial page (or home page) as the new tab is created is alos part of the session's history.

Now from here, if you visit three pages and traverse back to the home page of the current tab, and then inspect history.length, you'd get...

Can you figure it out?

Well, you'd get 4.

Let's try this out for real.

In the code below, we have two links that change the hash part of the URL, and a button which simply displays history.length:

<a href="#1">Go to #1</a>
<a href="#2">Go to #2</a>

<button>See history.state</button>
var button = document.querySelector('button');

button.onclick = function(e) {
    alert(history.length);
}

Try alternating between these links and then press the button to see the new length of the history stack.

Live Example

As you might have noticed, length is not a data property on the history object, rather it's an accessor property i.e. when we retrieve it, a corresponding getter function is called. When retrieved, it counts the total number of items in the history stack of the current session and returns that count.

state

The state property holds whatever value was set as state data for the current entry under inspection.

The two ways to configure state data are using the methods replaceState() and pushState(). We'll get to them soon in this chapter.

The state property was added to the history object as a means to access state data of the current entry without having to access the state property on a PopStateEvent object (which comes into existence only after popstate gets fired).

Let's see history.state in action:

In the document below, we call pushState() to push a new entry on the history stack with a different URL and with given state data.

Now if we reload this new entry, the popstate event won't fire (since a reload has been made) likewise state won't be accessible via PopStateEvent (the event didn't fire, so there would be no PopStateEvent object).

However, thanks to the history object, we could access the state data of the page by accessing history.state.

After the reload, when we access state, it's the same state object that we set for this page, while calling pushState().

So, in short, history.state could be really useful in certain occassions.

Methods

Time to move on to the real action — the methods of the History API.

back()

First, we'll unravel the rocket science behind the back() method.

Well, there is no rocket science!

When called, back() takes us back to the previous entry in the history stack. Is it the programmatic way to go to the previous entry.

Consider the code below:

<button>Go back</button>
var button = document.querySelector('button');

button.onclick = function() {
    history.back();
}

We have a button to go to the previous entry. This button is just an HTML button for the purpose, another being provided by the browser itself.

The moment we click it, we are taken to the previous entry in the history stack (if it exists).

Let's try this in the link below:

Live Example

forward()

The opposite action to going backwards in the browser is to go forwards.

The forward() method of the history object helps us in doing so.

When called, it takes us forward to the next entry in the history stack, if there is any, or otherwise has no consequence.

Consider the code below:

<button>Go forward</button>
var button = document.querySelector('button');

button.onclick = function() {
    history.forward();
}

Here we have a button to go to the next entry up in line. Open the link below, then visit some other link in the tab. Keeping things simple let's just say that you visit google.com. Now come back, and then press this button. You'd end up at the next entry in the history stack which is google.com.

Live Example

go()

The back() and forward() methods take us one step backward or once step forward in the history stack from where we currently are, respectively.

To go an arbitrary number of steps forward or backward, we could use the go() method.

Here's the syntax to go():

history.go(offset)

offset is the number of the entry to go to relative to the current entry. Positive values take forward, negatives take backward.

For instance, history.go(1) would take us to the first entry after the current one up in line; history.go(2) would take to the second entry after the current one up in line; and so on.

As stated before, negative values are possible as well. For instance, history.go(-1) would take us to the first entry before the current one, in line; history.go(-2) would take us to the second entry before the current one; and so on.

As you may have noticed, history.go(1) and history.go(-1) are equivalent to history.forward() and history.back(), respectively.

Calling go() with an offset value greater than the number of available entries would have no consequence — it'll be a silent call.

Anyways, let's see a quick example of using go():

<button>Go 2 steps forward</button>
var button = document.querySelector('button');

button.onclick = function() {
    history.go(2);
}

Live Example

replaceState()

The replaceState() method is meant to replace the current entry with a new one, and configure its state data.

To understand this definition completely, head over to JavaScript popstate event, where we explain replaceState(), pushState() and the related popstate event, to the core, in an extremely simple way.

Below shown is the syntax of replaceState():

history.replaceState(state, title[, URL]);
  1. state represents information binded with this replacing entry.
  2. title represents the title of the replacing entry. However, currently at the time of this writing, almost all web browsers except for Safari don't use this for any purpose at all — it's simply ignored.
  3. URL represents the URL of this replacing entry, to be displayed in the browser address bar. If omitted, it defaults to the current URL.

Let's see an example of replaceState():

alert('Previous state: ' + history.state)

history.replaceState({x: 1}, '')

alert('New state: ' + JSON.stringify(history.state))

Here, we first alert the current value of history.state, followed by calling replaceState() to configure the state data for the current entry. After this, we alert history.state again to see whether it has been updated or not.

In the last statement, we call JSON.stringify() in order to convert the state object into a representable format. Without this, it would be output as [object Object].

Live Example

pushState()

The pushState() method can undoubtedly be termed as the most useful of all methods of the History API. It allows a new entry to be added to the history stack without reloading the page.

This feature is used by most SPAs with AJAX-based navigations.

Syntactically, pushState() works exactly like replaceState() — there's no difference at all:

history.pushState(state, title[, URL]);
  1. state represents information binded with this new entry.
  2. title represents the title of the new entry. However, currently at the time of this writing, almost all web browsers except for Safari don't use this for any purpose at all.
  3. URL represents the URL of this new entry, to be displayed in the browser address bar. If omitted, it defaults to the current URL.

Below shown is an example of pushState().

<button>Call pushState()</button>
var button = document.querySelector('button');

button.onclick = function() {
    history.pushState(null, '', 'changed-url');
}

The moment we press the button, pushState() is called, and the URL of the address bar gets updated to that of the new entry. Moreover, the Back button on the browser works — it would take us back to our original entry.

Live Example

Calling pushState() multiple times with the same parameters would add as many entries to the history stack, even though they would all have the same URL. This can easily be confirmed by the code below: