HTML5 History API
Jakob Jenkov |
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.
Tweet | |
Jakob Jenkov |