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.
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:
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.
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.
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);
}
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]);
state
represents information binded with this replacing entry.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.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]
.
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]);
state
represents information binded with this new entry.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.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.
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: