August 2012

Volume 27 Number 08

Building HTML5 Applications - A History (API) Lesson

By Clark Sell | August 2012

History, in the context of a Web browser, has generally meant the back button, and it has never been easy to manage. It became even more of a challenge when AJAX became significant in Web applications and we started to see the typical Web site grow into more of a rich Web application. And as JavaScript became enabled—rather than disabled—by default, single-page Web applications emerged that made heavy use of client-side JavaScript. But there was no good way to deal with client-side storage, databases or even the general state between pages.

Now, however, HTML5 has taken client-side state management head-on and introduced a whole new set of APIs, including specifications such as IndexedDB, Local Storage and the History API, which I’ll focus on in this article.

In short, the goal of the History API is to provide a way for rich JavaScript applications to be better able to not only navigate the session history but also manage its state and provide first-class support for URLs. This means with a few simple API calls and events you can navigate around, pushing and popping state data to the session stack to affect, for example, how the back button operates, while maintaining a great URL structure.

The Problem

When you’re working on any solution, it’s important to understand the problem. The History API isn’t just about having some magic way to persist page state as the end user bangs on the back and forward buttons. While that’s certainly true, the real story lies deeper beneath the surface. From a user’s perspective, there are two major features of any browser: the URL and the navigation buttons (forward and back). Together these allow the user to request or navigate through a series of documents across the Internet.

Though these features have remained essentially the same over the years, behind the scenes things changed as AJAX caught on. What were once a series of documents became just a single file with AJAX calls behind the scenes. This was great; we could deliver a richer experience with just a little asynchrony, not to mention performance benefits and user experience improvements. But it also posed some problems; it became much more challenging to keep track of the URL, for example, to know where the browser should go when the user hit the back button or refresh.

I don’t think I can overstate the importance of the URL. URLs are permanent: users make favorites of them, search engines index them and companies market them. How could you manage them in the changing Web world?

Enter the hash (#) and hashbang (#!). Browsers consider anything after the # as a URL fragment identifier and never send it to the server. The hash was originally meant as a way for anchor tags to link inside the page, but it came to be used for AJAX--related activities. In the early days of AJAX, however, URLs with just a hash wouldn’t get indexed by a search engine. The solution was the hashbang, which gave Web applications a way to use and modify a URL and let search engines index it, without ever making a page request back to the server. URLs containing hashbangs, such as twitter.com/#!replies, became important with the advent of Web applications.

The New History API

Though this was all a great step forward, there wasn’t really any formal support from the browser. Web applications just played scripting games to make things work. The new History API focuses on giving Web applications the ability to “manage state,” which means keeping track of the sequence of documents that make up a session’s history. This lets you navigate the session history and persist state data—and deal with the URL properly. To start exploring this API, let’s look at the actual interface definition as specified by the World Wide Web Consortium, or W3C (https://www.w3.org/TR/html5/browsers.html#the-history-interface):

interface History {
  readonly attribute long length;
  readonly attribute any state;
  void go(optional long delta);
  void back();
  void forward();
  void pushState(any data, 
    DOMString title, 
    optional DOMString url);
  void replaceState(any data, 
    DOMString title, 
    optional DOMString url);
};

Not a complicated API, by any stretch.

To get an idea of how things really work, let’s start by doing some simple navigation between documents. Assume you have three real documents, a.cshtml, b.cshtml and c.cshtml, and you want to move around in all of them. Typically, you’d simply create an anchor tag <a href=“https://foo.com/a.cshmtl”>goto a</a> that the user clicks on, forcing the browser to drive though its normal page and server lifecycle. As that user clicks around a given Web site, he creates what’s called the session history.

There are two potential problems with this method, however. First, you’re forcing the browser to drive through its full page cycle, and even call the server; and second, you need a physical document that represents the URL.

AJAX solves part of the problem by allowing you to request just parts of a page, reducing the number of full-page requests and manually updating the URL to something like https://foo.com/\#\!a or https://foo.com/\#a. To accomplish something similar with the History API, you call window.history.pushState(state, title, url), passing along any state you want persisted, along with the title of the page and the URL to be displayed. Note that you’re not required to use a URL with a hash or hashbang; you can use just https://foo.com/a, even if “a” doesn’t physically exist.

By calling pushState, you’re creating the session history for that user’s session. The user can navigate as he sees fit and things will work as expected. When he hits the back button, he’s taken back to the previous URL as expected, and the URLs that were there before continue to exist, just as with any normal series of pages.

You also have hooks that let you navigate and look around the user’s session history. You can dynamically move the user forward and back through the stack, just as if the user himself were clicking the forward and back buttons.

A Real-World Example

Let’s make this concrete with a real-world example. I run a technology conference called That Conference (thatconference.com). The conference has many speakers, but I don’t want to create a page for each one of them. What I’d prefer to do is dynamically create a page for each speaker that appears real. I can do this easily with the History API.

As with any script-heavy Web application, I need data. Luckily, That Conference has a simple Representational State Transfer (REST) API I can call to get the speakers, thatConference.com/api/person. This call will yield an array of speakers for the given year in either JSON or XML. Figure 1 shows an item in that array.

Figure 1 A Speaker Profile

<PersonViewModel>
  <FirstName>Scott</FirstName>
  <LastName>Hanselman</LastName>
  <Company>Microsoft</Company>
  <Bio>
    My name is Scott Hanselman. I work out of my home office for Microsoft as a Principal Program Manager, aiming to spread good information about developing software, usually on the Microsoft stack. Before this I was the Chief Architect at Corillian Corporation, now a part of Checkfree, for 6-plus years. I was also involved in a few Microsoft Developer things for many years like the MVP and RD programs and I'll speak about computers (and other passions) whenever someone will listen.
  </Bio>
  <Twitter>shanselman</Twitter>
  <WebSite>https://www.hanselman.com</WebSite>
  <Gravatar>/Images/People/2012Speakers/ScottHanselman.png</Gravatar>
</PersonViewModel>

Data is no good without a way to see it. I need to set up a simple markup template I can use to dynamically create a page for each speaker. For this I’m going to use a framework called Knockout (knockoutjs.com). Knockout is a JavaScript library that helps developers use declarative bindings with the Model-View-ViewModel pattern. You’re not required to use Knockout for the History API, but I’m going to—and I’ll have a little fun along the way.

Because every speaker page is the same, I’m going to define a simple markup template in Knockout. I need to create a script block and tell the framework how to later populate it:

<script type="text/html" id="person-template">
  <div>
    <p>
      <strong>Name:</strong>
        <span data-bind="text: FirstName"></span>
        <span data-bind="text: LastName"></span>
    </p>
    <p>Company: <strong data-bind="text: Company"></strong></p>
    <p>Bio: <strong data-bind="text: Bio"></strong></p>
  </div>
</script>

Next, I need to populate the template. To do so, I call ko.applyBindings(someData), and Knockout will work its magic on whatever object I pass into applyBindings. With that, I have the basic mechanics in place to take a speaker object and populate the markup with its data.

My goal is a little more complex, though. What I really want is a series of pages that a user can flip through; a book of speakers, if you will. Here’s what needs to happen the first time the page is loaded:

  1. Get the JSON that represents the speakers.
  2. Bind the first item in the array to the Knockout template as the default.
  3. Call window.pushState, passing the appropriate arguments.

I’ve covered the first two steps already, so let’s talk about pushState. By calling window.pushState, you’re creating an item in the user’s session history. I’m going to call pushState and pass three items:

  • State: In my case this data is the array item I bound to the Knockout template.
  • Title: This is the title of the page, which will be the speaker’s full name.
  • URL: This is the URL for the page; in this case it will be something like thatconference.com/speakers/speaker/SpeakerFullName.

I’ve wrapped all of this logic in a method I called bind:

function bind (speakerID) {
  var speakerVM = new speakerViewModel(speakerID);
  var fullName = speakers[speakerID].FirstName 
    + speakers[speakerID].LastName
  window.history.pushState(speakerVM, 
    fullName, "/speakers/" + fullName); 
  ko.applyBindings(speakerVM);
}

Now I’ll add a couple of buttons to the speaker book to allow moving through the speakers:

<button id="prevSpeaker">previous speaker</button>
<button id="nextSpeaker">next speaker</button>

Of course, I need a couple of event handlers for the nextSpeaker and prevSpeaker buttons. To keep track of what speaker should be next, I’m going to create a simple counter that I’ll manipulate as the user navigates. The counter value is what I’ll pass to the bind method:

var counter = 0;
$('#nextSpeaker').click( function () {
  counter = counter + 1;   
  bind(counter);
});
$('#prevSpeaker').click( function () {
  counter = counter - 1;
  window.history.back();
});

At this point I have a page that loads with some default data, and as I click next, I continue to get the next speaker in the speaker array. If I call prevSpeaker, however, nothing happens. Something more is needed.

Events

When the back button (or a script) calls windows.history.back, the event onpopstate is fired. This is the hook into moving backward in a user’s session history. When onpopstate is fired, it passes along the state data given to pushState; in my case, it’s that one speaker.

Now I need to grab that state data and tell Knockout to bind it:

window.onpopstate = function (event) {
  console.log('onpopstate event was fired');
  ko.applyBindings( event.state );
};

With this, I can move back and forth in the session history as expected. You’ll now see the speakers change accordingly whether you press the browser’s back button or the previous-speaker button.

Now What?

As with most things, the devil is always in the details. I’ve just scratched the surface of the History API. I didn’t cover what to do if a user makes a favorite of a speaker and later visits the site or, for that matter, if he hits refresh while on one of those new speaker pages I just created.

I explicitly didn’t cover that scenario because there’s a multitude of ways to do so, but they depend on how you’ve structured your site and the technologies you’re using. If you subscribe to using # or #!, calling window.location.hash to get the URL fragment and then calling a service to retrieve the appropriate data for that hash and binding that to your markup might be all you need.

It’s important to note that while my solution creates an entire dynamic page, you can also use the History API for part of an existing page so the core of the page takes advantage of the server but part of the page uses the History API. You can find a great detailed example of exactly this at bit.ly/vOlB2U.

You should also implement feature detection in your Web application. Rather than basing actions on user agents, you should leverage a tool such as Modernizr (modernizr.com) to ask the browser what it can do. If a user’s browser doesn’t support a particular feature, you can use a polyfill—a shim that implements that feature for the browser. This can even be done for features such as CSS. For more information about feature detection, check out Brandon Satrom’s September 2011 article, “No Browser Left Behind: An HTML5 Adoption Strategy” (msdn.microsoft.com/magazine/hh394148).

AJAX changed the way Web sites interact on the Internet, and Web developers found creative solutions for turning standard Web sites into rich Web applications. The History API is here to help those script-heavy Web applications keep the core browser fundamentals intact.

Everything in this article was done on the Windows 8 Release Preview using Microsoft Web Matrix. You’ll find all of the code at on.csell.net/msdn-historylesson and a number of great resources for exploring the History API at on.csell.net/msdn-historylesson-linkstack.


Clark Sell works as a senior Web and Windows 8 evangelist for Microsoft outside of Chicago. He blogs at csell.net, podcasts at DeveloperSmackdown.com and can be found on Twitter at twitter.com/csell5.

Thanks to the following technical experts for reviewing this article: John Hrvatin, Mark Nichols, Tony Ross and Brandon Satrom