Speeding things up with sessionStorage

If you pass data between the client and server via Ajax and aren’t too worried about the content being fresh (perhaps it’s some static article text) then you might want to consider leaning upon the DOM Storage API to help speed things up, and in particular, sessionStorage.

What is sessionStorage?

At its most basic level sessionStorage is merely an in-memory object that allows you to get, set or remove items.

The key difference is that the data will stick with the user for their current session.

It allows strings of data to be saved, and this includes stringified JSON objects that can then be parsed back into objects when they are retrieved. This usage of sessionStorage (and indeed its close friend, localStorage) make it extremely useful for caching responses from the server.

Example

We can easily save some data:

  sessionStorage.setItem('data', JSON.stringify( {name: 'Simon'} ));

And then grab it again later:

var person = JSON.parse(sessionStorage.getItem('data'));

And if we’re done it can be thrown away:

sessionStorage.removeItem('data');

Basics covered.

Wrapping the API

Nine times out of ten you will find yourself storing and retrieving JSON. It’s just so damn useful. However one thing I find quite tedious is the constant need to stringify and then parse the storage values.

A way to alleviate the pain is to create a small wrapper object that will sit in front of sessionStorage (or localStorage, the APIs are identical) and handle it for you. Something a bit like this:

var storage = {
  storageAdaptor: sessionStorage,

  // Thanks Angus! - http://goo.gl/GtvsU
  toType: function(obj) {
    return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  },

  getItem: function(key) {
    var item = this.storageAdaptor.getItem(key);

    try {
      item = JSON.parse(item);
    } catch (e) {}

    return item;
  },

  setItem: function(key, value) {
    var type = this.toType(value);

    if (/object|array/.test(type)) {
      value = JSON.stringify(value);
    }

    this.storageAdaptor.setItem(key, value);
  },

  removeItem: function(key) {
    this.storageAdaptor.removeItem(key);
  }
};

I needed the above for a work project, so have since turned it into it's own mini-library - Storage wrap

You’ll notice that the API is exactly the same. It’s a very simple wrapper and will merely get rid of the hassle of using JSON.parse or JSON.stringify. We’ll look at handling browsers that don’t support sessionStorage in a bit.

Saving Ajax responses

Moving on now, let’s take a look at an example of saving a response from the server. We’ll start with a nice and easy click event:

$('.a-link').on('click', function(event) {
  loadContent(this.href);
  event.preventDefault();
});

We need something to use as a key for a storage object. You can use what you like here, but for ease the href attribute will do fine.

function loadContent(href) {
  // Check if we already have the data saved. Using
  // the wrapper object here so if it does exist
  // we'll already have a JSON object to use
  var json = storage.getItem(href);

  // If nothing is found in the storage then we'll get
  // null as the return value.
  // If it's a truthy value then we can assume it's all
  // good to pass over to our doSomething function
  if (json) {
    doSomething(json);
  } else {
    // Otherwise get the data...
    $.getJSON(href, function(json) {
      doSomething(json);
      // ...aaand remember to save it
      storage.setItem(href, json);
    });
  }
}

function doSomething(json) {
  // Deal with our data regardless of
  // where it came from
}

The above is quite a crude example, but it demonstrates the idea of extracting the doSomething logic into its own function and simply passing the JSON to it.

I implemented something very similar on this site recently. Previously the links to other parts of my portfolio would always request the JSON via Ajax regardless of how many times it had been loaded. If you navigate round now you’ll start to see pages you previously visited loading instantly. They even survive a refresh!

I really noticed an improvement when loading the site on a mobile device.

If you’d like to how I hooked it up then give it a look on GitHub. Suggestions for improvement are welcome :)

Faking sessionStorage for unsupported browsers

One of the good things about the way sessionStorage is implemented is that we can fake a version of it with a simple in-memory object. This means users of browsers without sessionStorage can feel some of the benefit, but of course they won’t see their data persist:

var storage;
if (Modernizr.sessionstorage) {
  storage = sessionStorage;
} else {
  storage = {
    items: {},
    getItem: function(key) {
      return this.items[key];
    },
    setItem: function(key, value) {
      this.items[key] = value;
    },
    removeItem: function(key) {
      delete this.items[key];
    }
  }
}

Yeah, it’s quite crude but simulates the effect nicely.

As an aside, if you don’t have Modernizr you can perform a simple check for sessionStorage with the following:

if ('sessionStorage' in window) {  }

If we put our little shim together with the storage wrapper then it makes for nice little utility to carry round to different projects. Check this repo for a full example. As you can probably see, it would be trivial to swap out sessionStorage for localStorage.

If you’re also in need of JSON support then look no further than Crockford’s json2.js library.

And we’re done!

That’s it. A pretty simple example but I think a lot of sites can benefit from caching some of their data into the browser storage. If you’d like a more fully-fledged solution then two libraries I can recommend are:

  • depot.js - Nice for saving thing as records. Supports localStorage/sessionStorage or any object that uses the same API.
  • amplify.store - Part of the amplify.js library. Supports the same things as depot.

Go forth and cache.