Note: I originally wrote this post for the Humaan blog.
In case you’ve been living under a rock, LocalStorage is a JavaScript API
that allows you to store content in the browser’s cache and access it later on when you need it. Similar to how cookies work, but you’ve got much more than ~4KB (the maximum size for a cookie). That said, while there’s no size limit for each key/value pair in LocalStorage, you’re restricted to around 5-10 MB for each domain. I say 5-10 MB because as per usual, different browsers have different maximum limits. Classic!
Recently we’ve had the opportunity to use LocalStorage for our content. I quite liked it at first because it was very simple to use, but it wasn’t long before we ran into issues. Get this: LocalStorage stores content indefinitely, while SessionStorage stores content for the lifetime of the browser session… and there’s no middle ground. What if I want to store something for 30 minutes? Tough luck!
To make things more interesting, even though some browsers report that they have LocalStorage (I’m looking at you, Safari in Private Browsing mode!) you can’t actually use it (trying to write to/read from it will throw an exception).
Bearing these in mind, we decided to write a small custom library that gives you the ability to use expiries, fail-safe LocalStorage detection, and callbacks for when fetching data doesn’t run as expected.
Our most important method is supportsLocalStorage().
This method makes sure we can actually use LocalStorage, bypassing the likes of our aforementioned friend, Safari in Private Browsing mode. Here we wrap the LocalStorage set/get in a try/catch statement to catch any read/write exceptions.
Then, we call supportsLocalStorage() at the start of all the other methods to ensure we can actually use LocalStorage. If not, just abort any call early.
Now let’s look at the setter, setItem():
Not a particularly complex method when you think about it. The first two parameters are the key and value for the content you want to store. If you’re experienced with JavaScript (or any other language that uses the concept of keys and values with arrays/objects/hashes/etc) you’ll understand what they mean with no problems. The value can be anything that the core JSON
object can stringify.
The last parameter, expiry, is an optional numeric parameter that if supplied, is used as the lifespan of your key/value combination. When combined with our LocalStorage getter, we can use this expiry value to know whether we should bust the key/value next time we go to fetch it!
Be warned: there’s not a tremendous amount of validation ensuring legitimate numbers are used as an expiry – just provide an integer for the number of seconds you want your key/value combination remaining valid in the LocalStorage cache. It’s easy, you’ll be fine.
You’ll notice this method returns true without checking whether your item was actually set in LocalStorage. Unfortunately the API
itself doesn’t return a “save” response, so we have to assume the best and say that it did. But if this isn’t good enough, you can write a fetch method to confirm your suspicions. To me, that seems like overkill because the ideal time to check your item is when you’re actually fetching the data to use it. LocalStorage, like cookies, can be easily modified by the end user, so don’t assume that just because you saved it, it’ll be there the next time you go to pull it.
Our next very important method is our getter, getItem()!
This method is a little more complex. Obviously, the first parameter is the key name for the value you want to retrieve. The second value, however, is a callback you can provide to handle when a value doesn’t exist in LocalStorage. While a callback isn’t technically necessary, because LocalStorage calls aren’t asynchronous, I prefer using them as I feel it makes my code neater.
In your closure, your value in LocalStorage will be returned if it exists. Otherwise, if it doesn’t exist or if it has expired, you’ll get a null return value. This was the cleanest way I could come up with for handling both scenarios, which unfortunately does mean having to do an if/else statement in the closure to check whether the response is null or not.
Finally, our last method is a simple remover method.
The native LocalStorage method doesn’t return a status on whether the operation was successful or not, so I mimic the core method by returning void. Again, you could use a getter to ensure data has been removed, but I don’t really see a point in ensuring the core method has worked correctly.
Both of these examples use the expiry feature in the LocalStorage helper. If the devices key/value doesn’t exist in LocalStorage, we’ll fetch it via AJAX
and save it to LocalStorage for 1 hour (60 minutes), then either pass it off to another function or return it.
And there you have it! Check the Bitbucket snippet link for the latest updates as we continue to use and refine our LocalStorage helper.