Design by Pelling
01252 341 730

Archived Blog 

HTML5 pushState() Tutorial

HTML 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:

  1. Add event listeners that listen for a specific event such as click, mouseover etc…
  2. When the above event is triggered, we’ll use XMLHttpRequest to grab the new content.
  3. The new content will then be used to overwrite the old content.
  4. 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

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;
});
});

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.

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);
});
}

The getResults() function does 3 things:

  1. Removes the “active” class from all list items (line 2).
  2. Adds the “active” class to the list item that contains the link that was clicked (line 3).
  3. Uses $.get() method to grab the new content. It sends a request to ajax.php, with page, a numeric value that represents what page we want to load. It’ll then replace the HTML content of div#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:

window.addEventListener("popstate", function(){
e = $('a[href="'+location.pathname.split("/").pop()+'"]');
getResults();
});

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.

Want to hire us? We'd love to hear from you

Pelling have over 50 years combined experience designing and coding websites. We work with companies of all sizes, from one-man bands to multinational blue chip companies. We endeavour to provide high quality service, while maintaining a competitive price.

We pride ourselves in our level of customer service, which we believe is the main reason behind our high customer retention rates.

  • Kt

    Awesome tutorial! I’ve been trying to get my head around the push and pop states and after reading this I finally understand and was able to get my code working perfectly!! Thanks so much!

    • hodaka

      Hi Kt,

      No problem at all. I’m super pleased that you found it helpful!

      Hodaka