History Lite: A lightweight Ajax browser history module for YUI 3

History Lite is a new YUI 3 Gallery module that provides an extremely lightweight (856 bytes minified and gzipped) and flexible Ajax browser history API. I originally wrote History Lite as a YUI 2 module for use on Yahoo! Search, and when the YUI 3 Gallery was announced recently, I jumped at the chance to port it to YUI 3 and release it publicly.

What’s it For?

Ajax applications often involve client-side interactions that change the contents or state of the page without performing a full page refresh. Unfortunately, browsers don’t record new history events for this kind of interaction, which means that the back/forward buttons cannot be used to navigate through the client-side changes. It also means that bookmarks and copied/pasted URLs will not return the user to the actual page state they might expect.

History Lite and other similar libraries provide APIs that Ajax applications can use to programmatically add state information to the browser’s history by manipulating the document’s location hash (the part of the URL after the # character), thus preserving the expected back/forward button behavior. This also results in copyable, bookmarkable URLs that allow an Ajax application to restore its state when it’s loaded.

YUI 2 and 3 already provide an excellent History utility written by my colleague Julien Lecomte. However, it has a few inconvenient requirements — an <iframe> must be added to the page, and all state parameters must be pre-registered before the module is initialized — which are necessary in order to provide full support for IE6 and IE7. This makes it a bit heavy for performance-sensitive use cases (especially since the <iframe> causes another HTTP request) and results in an API that can be difficult to share between multiple unrelated modules that coexist on a page.

History Lite provides only partial support for IE6 and IE7, which makes it possible to have a much smaller implementation and a more flexible API that doesn’t require any pre-existing markup or initialization. If supporting older versions of IE is critical for you, then you should use the YUI History utility. However, if you’re willing to do without legacy IE support, History Lite is a good alternative.

Usage

History Lite is hosted on the same Yahoo! CDN as YUI 3 itself, so you don’t even need to download anything to use it. Just tell YUI where to find it and it’ll be loaded automatically on demand:

<script src="http://yui.yahooapis.com/3.0.0/build/yui/yui-min.js"></script>
<script>
  YUI({modules: {
    'gallery-history-lite': {
      fullpath: 'http://yui.yahooapis.com/gallery-2009.12.15-22/build/gallery-history-lite/gallery-history-lite-min.js',
      requires: ['event-custom', 'event-custom-complex', 'node']
    }
  }}).use('gallery-history-lite', function (Y) {

    // Y.HistoryLite is now available to your code.

  });
</script>

History Lite doesn’t require any initialization, and the API consists of the add() and get() methods and the global history-lite:change event. Yep, that’s really the entire API!

Subscribe to the history-lite:change event to be notified when the history state changes. This occurs whenever a history parameter is added, modified, or removed. This example just logs stuff to the console to demonstrate how things work, but typically this is where you would implement any logic necessary to change the state of your application:

Y.on('history-lite:change', function (e) {
  // Properties on e.changed represent new or changed history parameters.
  Y.each(e.changed, function (value, name) {
    console.log(name + ' changed to "' + value + '"');
  });

  // Properties on e.removed represent history parameters that have been
  // removed.
  Y.each(e.removed, function (value, name) {
    console.log(name + ' was removed');
  });

  // The get() method returns the current value of the specified history
  // parameter. If you call get() without specifying a parameter name,
  // it'll return an object containing all current history parameters and
  // their values.
  console.log('current value of foo is ' + Y.HistoryLite.get('foo'));
});

In addition to listening for the history-lite:change event, it’s also a good idea to call get() when the page loads in order to restore state from a bookmarked or copied/pasted URL.

Use the add() method to add new entries to the browser history. Each call to add() will modify the document’s location hash, thus triggering the history-lite:change event:

// The add() method accepts an object containing key/value pairs of
// history parameter names and values. Each call to add() creates a new
// browser history entry.
Y.HistoryLite.add({foo: 'bar', baz: 'quux'});

// The add() method will also accept a query string.
Y.HistoryLite.add('foo=kittens&bar=puppies');

// A null or undefined value causes that parameter to be removed from
// the history state.
Y.HistoryLite.add({foo: null, baz: 'monkeys'});

Whenever you want your application to perform a state-changing action, use add() to trigger a change event and then perform the actual state change in the event handler (or in code called from the event handler). This enforces code modularity while also ensuring that state changes are explicitly tied to history events.

Supported Browsers

IE6 and IE7 are partially supported in that state changes and back/forward navigation within a single pageview will work, and bookmarked URLs will restore state. However, after navigating away from a page and then returning using the back/forward buttons, previous Ajax history from within that page will be lost.