2016-04-28

In this tutorial we’re going to build a website with beautifully smooth transitioning pages, without the usual aggressive page refresh. Navigate through the pages in the demo to see what I mean.

To achieve this effect we’ll use the History Web API. In a nutshell, this API is used to alter the browser history. It allows us to load a new URL, change the page title, then at the same time record it as a new visit in the browser without having to actually load the page.

This sounds confusing, but it opens up a number of possibilities–such as being able to serve smoother page transitions and give a sense of speediness which improves the user experience. You have probably already witnessed the Web History API in action on a number of websites and web applications, such as Trello, Quartz, and Privacy.

Before we go any further, let’s first look into one particular API that we are going deploy on the website.

The History Web API, in Brief

To access the Web History API, we first write window.history then follow this with one of the APIs; a method or a property. In this tutorial we’ll be focusing on the pushState() method, so:

As you can see from the above snippet, the pushState() method takes three parameters.

The first parameter, state, should be an object containing arbitrary data. This data will then be accessible through window.history.state. In a real world application, we would pass data like a page ID, a URL, or serialized inputs derived from a form.

The last two parameters are title and

url. These two change the URL and the document title in the browser, as well as record them as a new entry in the browser history.

Let’s dissect the following example to better understand how the pushState() Method works.

In the above code, a link attached with the click event then deploys the pushState() method. As we click on the link, we expect the code to change the document title and the URL:

And it does; the screenshot shows the URL is changed to “about/” as defined in the pushState() method. And since the pushState() method creates a new record in the browser history, we are able to go back to the previous page through the browser’s Back button.

However, all the browsers in this example are currently ignoring the title parameter. You can see from the screenshot the document does not change to About - Acme as specified. Furthermore, calling the pushState() method won’t also trigger the popstate event; an event which is dispatched every time the history changes–something we need! There are a few discrepancies on how browsers handle this event, as stated in MDN:

“Browsers tend to handle the popstate event differently on page load. Chrome (prior to v34) and Safari always emit a popstate event on page load, but Firefox doesn’t.”

We will need a library as a fallback to make the History Web APIs work consistently across the browser without any hurdles.

Meet History.js

Since the pushState() method does not work to its full potential, in this tutorial we are going to leverage History.js. As the name implies, this JavaScript library is a polyfill, replicating the native History APIs that work across different browsers. It also exposes a set of methods similar to the native APIs, albeit with few differences.

As mentioned earlier, the browser native API is called through the history window object with the lowercase “h”, while the History.js API is accessed through History with the uppercase “H”. Given the previous example and assuming we have the history.js file loaded, we can revise the code, as follows (again, notice the uppercase “H”).

I hope this brief explanation is easy to understand. Otherwise, here are some further references if you want to learn more about the Web History API.

History API

Manipulating the Browser History

An Introduction to the HTML5 History

Building Our Static Website

In this section we won’t discuss each step needed to build a static website in detail. Our website is plain simple, as shown in the following screenshot:

You don’t have to create a website that looks exactly the same; you are free to add any content and create as many pages as you need. However, there are some particular points you need to consider regarding the HTML structure and the use of id and class attributes for some elements.

Load jQuery and History.js within the document head. You may load it as a project dependency through Bower, or through a CDN like CDNJS or JSDelivr.

Wrap the header, the content, and footer in a div with the ID wrap; <div id="wrap"></div>

There are a few navigation items on the website header and the footer. Each menu should be pointing to a page. Make sure the pages exist and have content.

Each menu link is given page-link class which we will use for selecting these menus.

Lastly, we give each link a title attribute which we’ll pass to pushState() to determine the document title.

Taking all this into account, our HTML markup will roughly look as follows:

When you are done building your static website we can move on the main section of this tutorial.

Applying the History Web API

Before we begin writing any code, we need to create a new file to hold our JavaScript; we’ll name it script.js and load the file in the document before the body closing tag.

Let’s add our first piece of code to change the document title and the URL upon clicking the menu navigation:

I’ve split the code apart into several numbered sections. These will make it easier for you to pinpoint the code with the following reference:

On the first line, we select the element, <div id="wrap"></div>, that wraps all of our website content.

We attach the click event. But, as you can see above, we attach it to the #wrap element instead of attaching the event directly on every menu navigation. This practice is known as event delegation. In other words, our #wrap element is responsible for listening to click events on behalf of .page-link.

We’ve also added event.preventDefault() so that the users will not be directed to the page in question.

If the clicked menu URL is the same as the current window we do not need to proceed to the next operation, simply because it is not necessary.

The pageTitle variable contains the title format, derived from the link title attribute or the link text. Each page title follows {Page Title} — Acme convention, except for the home page. “Acme” is our fictitious company name.

Lastly, we pass the pageTitle and the page URL to the History.js pushState() method.

At this point, when we click on the menu navigation, the title as well as the URL should change accordingly as shown below:

Yet the page content remains the same! It is not updated to match the new title and the new URL.

Content

We need to add the following lines of code to replace the actual page content.

Again, the code here is split into several numbered sections.

The first line of the code listens to the History change performed via the History.js pushState() method and runs the attached function.

We retrieve the state changes, containing various data like a URL, title, and id.

Through the jQuery .get() method we retrieve the content from the given URL.

Lastly, we sort out the element with an id named wrap from the retrieved content, and eventually replace the current page content with it.

Once it’s added, the content should now be updated when we click on the menu navigation. As mentioned, we are also able to access visited pages back and forth through the browser Back and Forward buttons.

Our website is presentable at this point. However, we would like to step further by adding a little animation to bring the page to life and, finally, our website feels more compelling.

Adding Animation and Transitions

Animation in this situation need only be simple, so we’ll write everything fro scratch, instead of loading animations through a library like Animate.css, Motion UI of ZURB, or Effeckt.css. We’ll name the animation slideInUp, as follows:

As the name implies, the animation will slide the page content from bottom to top along with the element opacity. Apply the animation to the element that wraps the page main content, as follows.

The transition from one page to another one should now feel smoother once the animation is applied. Here, you may stop and call it a day! Our website is done and we are ready to deploy it for the world to see.

However, there is one more thing that you may need to consider adding, especially for those who want to monitor the number of visits and the visitors’ behavior on your website.

We need to add Google Analytics to track each page view.

Google Analytics

Since our pages will be loaded asynchronously (except for the initial page loaded) tracking the page view number should also be done asynchronously.

To begin with, make sure you have the standard Google Analytics added within the document head. The code usually looks something as follows:

Then we need to adjust our JavaScript code to include the Google Analytics tracking code so that every page loaded asynchronously will also be measured as a page view.

We have several options. First, we can start counting when the user clicks a navigation link, or when changing the page title and URL, or when the content of the page has been fully loaded.

We’ll opt for the latter, which is arguably the most authentic, and in doing so we leverage the jQuery promise() method after we change the page content, as follows:

That’s all it is, we will now have the page view recorded in Google Analytics.

Wrapping Up

In this tutorial we have improved a simple static website with Web History API to make the page transition smoother, the load faster, and overall deliver a better experience to our users. At the end of this tutorial, we also implemented Google Analytics to record user page view asynchronously. Additionally, our website is perfectly crawl-able by search engine bots since it is, as mentioned, just a simple HTML website.

This was a meaty tutorial, explaining lots of things like CSS Animation, jQuery Ajax, and jQuery Promise. Here’s a handful of references for you to look into, to reinforce what you’ve learned.

A Beginner's Introduction to CSS Animation

Quick Tip: JavaScript Event Delegation in 4 Minutes

AJAX for Front-End Designers

Wrangle Async Tasks With jQuery Promises

Demystifying Google Analytics

Lastly, don’t forget to visit the demo site of this tutorial as well as the source code in the repository.

Show more