Design Method
HTML5 pushState() Tutorial
When it comes to providing visitors with a good user experience, having short page load times is one of the top priorities. There are a large number of ways this can be achieved, such as combining and minifying CSS & JS files, embedding base64 images, utilising the browser cache etc…
The method I’d like to introduce you to today utilises HTML5’s history API. This API allows you to manipulate the browser history via script. You can change the URL in the browser without triggering a page refresh, and also listen for when a user presses the back button.
Why is this useful?
There will be situations where two or more of your pages contain a significant amount of duplicate code. Even on a site like ours, you’ll notice that the header, footer and sidebar are exactly the same on a lot of pages. Each time we load a page, we’re loading redundant code that we don’t really need.
With HTML5’s history API, or more specifically the pushState()
method and popstate
event, we’re able to handle things slightly differently. Here’s the new approach we can take:
- Add event listeners that listen for a specific event such as click, mouseover etc…
- When the above event is triggered, we’ll use XMLHttpRequest to grab the new content.
- The new content will then be used to overwrite the old content.
- We’ll use the
pushState()
method to update the URL.
Sounds good! Are there any downsides though?
Yep! According to caniuse.com, only 73.45% of browsers support this technology at the time of writing. Therefore you’ll definitely have to have some kind of fallback functionality in place. Saying that however, something like this should be used to enhance your website, rather than for core functionality. Therefore I recommend you build your website normally, and then add the necessary JavaScript to utilise the pushState()
method.
Getting started
In today’s demo, I’ll be utilising this method with pagination so that we can load the different pages of results without having to reload the entire page.
Here’s a demo page I prepared with the conventional “load everything” mentality. As you browse through the pages, you’ll notice that the only part that actually changes is the pagination results.
In comes the JavaScript
[code lang=”js”]
var e, p;
$(function(){
p = $(‘.pagination’);
p.find(‘a’).click(function(){
e = $(this);
if(!e.closest(‘li’).hasClass(‘active’)){
getResults();
history.pushState(null, null, e.attr(‘href’));
}
return false;
});
});
[/code]
Hopefully you’ll have experience working with jQuery, and the above will make sense.
I started by defining the variables e
and p
in the global scope (line 1). This is so that I can access these variables later on, within the getResults()
function. I then add a click event listener on links within the pagination (line 4). When a link is clicked, it’ll run the getResults()
function (line 7) and also use the pushState()
method to update the URL (line 8).
The pushState()
method takes 3 parameters, which are state
, title
and URL
. The last parameter is the one we’re interested in, and tells the browser what the new updated URL should be. In this example I’ve only specified the URL, but you can read more about the pushState()
method on MDN.
Two quick things to note are on line 6 and line 10. When a link is clicked, the code on line 6 will check if it’s parent list item has the “active” class. The following code is prevented from running if it does have the class. This is to stop any unnecessary loads, e.g. loading page 2 when it’s already showing page 2.
In addition the code on line 10 prevents the default browser functionality, i.e. loading the target URL.
[code lang=”js”]
function getResults(){
p.find(‘li’).removeClass(‘active’);
e.closest(‘li’).addClass(‘active’);
$.get(‘ajax.php’, {page: e.attr(‘href’).replace(‘.php’, ”)}, function(data){
$(‘#results’).html(data);
});
}
[/code]
The getResults()
function does 3 things:
- Removes the “active” class from all list items (line 2).
- Adds the “active” class to the list item that contains the link that was clicked (line 3).
- Uses
$.get()
method to grab the new content. It sends a request toajax.php
, withpage
, a numeric value that represents what page we want to load. It’ll then replace the HTML content ofdiv#results
with the response (lines 4-6).
Going back
We’ve now got the pushState()
sorted. When a visitor clicks on the pagination links, the page will only update the necessary parts of the page. What we haven’t addressed is what happens when they click the back button.
What we need is the ability to listen out for when a user clicks the back button within the browser. Luckily for us, the HTML5 history API has an event that’s fired when a user does just that. This is the popstate
event, which we can listen for by attaching the listener to the window
object. This can be done with the following code:
[code lang=”js”]
window.addEventListener("popstate", function(){
e = $(‘a[href="’+location.pathname.split("/").pop()+’"]’);
getResults();
});
[/code]
This essentially just calls the getResults()
function when the event is fired. You’ll notice that I’ve set e
(a variable in the global scope) using a CSS attribute selector, as the getResults()
function requires this to be set.
Now when a visitor clicks the back button, the appropriate page is shown within the results section!
And there we have it! A fully functional pagination that utilises the HTML5 history API! Take a look at the complete example here.
Areas of improvement
To further refine this example, you could:
- Consider switching the response from
ajax.php
to JSON, as this would help to further reduce the size of the response. - Depending on how much jQuery you actually use on the website, you could remove jQuery and switch to pure JavaScript.