Manipulating the browser’s history can be quite useful when you are creating a single page application which relies exclusively on AJAX to show different content. You can manipulate the browser’s history to tell the user’s browser that the user is located on a different page despite the fact that he is not and you can catch requests to that URL so the user can share the content he is currently viewing.
This enhances the user’s experience by enabling him to come back to particular parts of your SPA whenever he wants and to share particular content from your website with the people in his circle.
Browser support
The new additions to the History API – history.pushState, history.replaceState and history.popState were added with HTML5 but have a pretty decent support globally. Internet Explorer 10 and above supports them, Chrome and Firefox 31 and above supports the methods, as well as Opera 27+ and Safari 7+. They run on the native Android browser with version 4.3+, iOs Safari 7.1+ and Android’s Chrome browser 41+.
Session history management for SPAs
We will create a basic SPA to show the implementation of a SPA that has many pages and look like a normal web app.
First, we create our index.html with a menu and some dummy text.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>My Page</h1>
<a href="#" data-page="clients">Clients</a>
<a href="#" data-page="products">Products</a>
<a href="#" data-page="contact">Contact</a>
<div class="content">
<h2>Home Page</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aliquid earum, fugit impedit maiores nulla odio
perspiciatis quod voluptas voluptates? Ab corporis ea minus optio.</p>
<!-- More paragraphs below -->
</div>
Then, we create a partials directory and add 3 pages: clients.html, contact.html and products.html
Here is how one of the partials look like:
contact.html
<h2>Contact us</h2>
<p>Lorem ipsum dolor.</p><p>Aspernatur, recusandae, veritatis!</p>
Now, for the fun part:
We create a function that loads the partials with an ajax call. It takes as arguments the partial that it should load (the page).
history.pushState takes three arguments – the first is an object or other variable that would be passed to your handler for an onpopstate event. Onpopstate is triggered whenever the user uses the browser’s back button and attempts to go to the previous page – in that case you would want to add a listener for onpopstate events and show them the page they desire. Your onpopstate handler would be passed an event object which would have in it the first argument that you passed to history.pushState().
The second argument of pushState is the title and the third represents the manipulation of the URL that you want.
In our case, we want to add a hash with the page/partial’s name.
function loadPartial(page,add) {
xhr = new XMLHttpRequest();
xhr.open("GET", 'partials/' + page + ".html");
history.pushState({page: page}, page.toUpperCase(), "#" +page );
xhr.onreadystatechange = function() {
document.getElementsByClassName("content")[0].innerHTML = xhr.responseText;
}
xhr.send();
}
Next, we loop over each anchor in the document (you could loop over each anchor in some div or other container if you want), get the data-page attribute of the anchor and load the partial named after the value of data-page by calling our loadPartial() function. We pass the page to the loadPartial function.
var anchors = document.getElementsByTagName("a");
for (var i = 0;i < anchors.length;i++) {
anchors[i].onclick = function() {
var page = this.getAttribute("data-page");
loadPartial(page);
return false;
}
}
Then, we catch any popstates and call loadPartial:
window.onpopstate = function(evt) {
if (evt.state && evt.state.page) {
loadPartial(evt.state.page);
}
}
Notice that some versions of Chrome trigger popstate when the page is first visited with a null evt.state so first we check if evt.state.page exists. Then, we load the partial (the object we passed as a first argument was called page so we could find at evt.state.page).
Finally, when the page is first opened we check if there is a hash in the URL and if there is we try to load the partial with that name:
loadPartial(window.location.hash.replace("#", ""));
}
Now, users can both open links, experiencing a website with pages and they can also go back and forth between our pages or share their URLs so others could see the exact same content your users wanted them to see (not the homepage)
Conclusion
I hope now you have seen how to make SPA that are more usable and that provide better user experience. Have you ever used the History API before to make this kind of SPAs? What did you built?
If you are planning on building something using this technique, I would be very happy if you share what you have built with me here in the comments.