Storing Data the Simple HTML5 Way (and a few tricks you might not have known)

by .

Yes indeed people, it’s your favourite HTML5 Doctor with JavaScript knowledge who lives in Brighton near a golf course! I’m also shooting for the longest title we’ve published so far – and I think I’m in the lead.

This post is about the Web Storage API. Technically it’s been shifted out of the HTML5 specification and can now be found in it’s very own dedicated spec. But if it counts at all – it used to be part of the Web Applications spec.

Web Storage is a very, very simple way to store data in the client – i.e. the browser. What’s more, the support is fabulous: IE8 and upwards has support natively, and there’s lots of good polyfills in the wild already.

This post however will just focus on the features of Web Storage and hopefully show you a trick or two you may not have known about.

What Web Storage Does

Using JavaScript, Web Storage makes it possible to easily store arbitrary values in the browser. Storage is different from cookies in that it’s not shared with the server. It’s also different from cookies in that it’s dead simple to work with.

There are two versions of Web Storage: local and session. Local means that it’s persistent, and session is simply that the data is lost when the session ends (i.e. when you close you browser window or tab). It’s also worth noting that a session is tied to a single browser window or tab. The session doesn’t leak out in to other open windows on that same domain.

Any data stored is tied to the document origin, in that it’s tied to the specific protocol (http or https, etc), the host (html5doctor.com) and the port (usually port 80).

Get Storing

The API for localStorage and sessionStorage is exactly the same, and distills down to the following methods:

  • .setItem(key, value);
  • .getItem(key)
  • .removeItem(key)
  • .clear()
  • .key(index)
  • .length

You can probably guess what most of those methods do, maybe not the last two, but ignore all that syntax for a moment.

Due to the way that the storage API has been defined, the set/get/remove methods all are getters, setters and deleters. What that means to you is you can use localStorage as follows:

localStorage.name = 'Remy';

If you tried using the link above, this will save a property called “name” against localStorage. Completely closing your browser, and going back to http://jsconsole.com and test the name property again:

console.log(localStorage.name);

Still holds the value doesn’t it? Pretty easy eh? And no faffing around with cookies, for that, we can be thankful!

Equally deleting data is very easy:

delete localStorage.name;
console.log(localStorage.name);

You’ll see the value is undefined.

What’s happening under the hood is that when you set a property on localStorage (or sessionStorage) it’s calling the .setItem method. When you get a property, it’s calling .getItem and when you delete, it’s calling removeItem. That way you can treat the Web Storage interface as any other regular object in JavaScript, except you’ll know it’ll hang around after the page has unloaded.

Hey everyone! I’m storing some data!

That’s what your browser does when you store some data. It announces it via events.

When you set some data on localStorage or sessionStorage, an event, called “storage”, fires on all the other windows on the same origin.

To listen for events the following code is used:

function storageEvent(event) {
  event = event || window.event; // give IE8 some love
  alert('Yo people! Something just got stored!');
}

if (window.attachEvent) { // ::sigh:: IE8 support
   window.attachEvent('onstorage', storageEvent);
} else {
    window.addEventListener('storage', storageEvent, false);
}

Note that as IE8 has support, if you don’t want to use onstorage you’ll want to double up your event listeners with attachEvent.

Clearly a massive alert box isn’t useful, but what is useful is what you capture inside of the event object:

  • key – the property name of the value stored
  • newValue – the newly set value (duh!)
  • oldValue – the previous value before being overwritten by .newValue
  • url – the full url path of the origin of the storage event
  • storageArea – the storage object, either localStorage or sessionStorage

With this information, you can do a lot with the storage event, like perhaps keep tabs that are open synchronised. Here’s a simple, contrived example of using localStorage to echo a value across different windows on html5demos.com.

Or for a real world example, Font Deck recently implemented this very feature, if you change the sample text in one window, all other open tabs (or windows) mirror that same sample text.

Gotchas to watch out for

The main gotcha with Web Storage is that although the specification used to say that any object type can be stored, in fact all browsers currently coerce to strings. That means if you want to store a JavaScript object (or an array perhaps), you’ll need to use JSON to encode and decode:

var doctors = [
  'rem', 
  'rich_clark', 
  'brucel', 
  'jackosborne', 
  'leads', 
  'akamike', 
  'boblet'];
localStorage.doctors = JSON.stringify(doctors);

// later that evening…
var html5docs = JSON.parse(localStorage.doctors);
alert('There be ' + html5docs.length + ' doctors in the house');

† Edited thanks to feedback from zcorpan

When working with storage events, a couple of things to also watch out for – this is on purpose of course, but still just about qualify for gotcha status:

  1. The event only fires on the other windows – it won’t fire on the window that did the storing.
  2. The event won’t fire if the data doesn’t change, i.e. if you store .name = ‘Remy’ and set it to ‘Remy’ again it won’t fire the storage event (obviously, since nothing was stored).

and finally…

If you want to use web storage in browsers like IE6 and IE7 (or rather than want: need to in your particular case) – then there’s no shortage of polyfills for Web Storage. However be wary that you’ll need to stick to the regular setItem syntax rather than being able to use the sexy setter/getter syntax – and I’ve yet to see a polyfill that supports the storage events.

Maybe that isn’t a problem for you though – maybe the sheer simplicity and spiffingness of Web Storage is enough to just support IE8 and above with this enhanced client storage model.

28 Responses on the article “Storing Data the Simple HTML5 Way (and a few tricks you might not have known)”

zcorpan says

> The main gotcha with Web Storage is that although the specification says that any object type can be stored,

The spec changed.

Also, maybe you should mention that Web storage suffers from being racy if the browser uses different processes for different tabs. Which is the reason some browser developers don’t want anyone to ever use Web Storage. (Cookies suffer from this problem as well, though.)

Remy Sharp says

@zcorpan – shame, the browsers should have changed, but I guess we go with what we’ve got, right?

Re: “racy” – can you elaborate a little, I don’t think I follow – but maybe because I can’t think of an example of what you mean perhaps.

Cheers!

Kamil Trebunia says

I believe that by “racy” zcorpan meant “susceptible to race condition”.

Tab Atkins Jr. says

By “racy”, he’s referring to race conditions, where different things can occur based on the order that supposedly-independent things ran in.

For example, say you were storing a counter in localStorage, and wanted to increment it. You might do something simple like this:

localStorage.foo = localStorage.foo+1;

In between the time you retrieved the value and stored the modified value, another tab running in a different process might have stored a *different value* there, which you would overwrite. That means lost increments on your counter!

Of course, the time window in this example is miniscule, but more realistic examples are easy to imagine where you pull out a bit of data, do some expensive computation that takes a couple cycles, then store it back. There’s a greater possibility for problems there.

Of course, the exact same problem exists with cookies, which doesn’t seem to have bothered people much, so this probably isn’t a big deal in practice. Every once in a while you might have data corruption, is all.

Other techs like IndexedDB solve this problem by implementing proper transactions and write-locks and such, but they’re not nearly as simple as localStorage.

Jeremy Keith says

It strikes me that this is the perfect solution to that “God damn it! I just lost everything I was typing into a textarea!” problem. It would be a nice enhancement for the browsers that support Web Storage (storing the contents of the textarea periodically) and for browsers that don’t …well, that’s the current situation for typing things into textareas on the web anyway.

Someone’s probably made a WordPress extension already, right?

Conor Clafferty says

// Resubmitted to show code blocks

This race condition isn’t a problem, see sixrevisions article:
http://sixrevisions.com/html/introduction-web-storage/
under the heading “Web Storage Events”

By racy he means that it will cause a race condition. The concern is as browsers now implement multithreading, threads can cause problems when saving data. Here’s a good example

var myarray = [a,b];
var first = myarray.splice(0,1);
localStorage.first = first;

You would expect the following:

localStorage.first == a; //true

When a race condition occurs we could find that this happens

localStorage.first == b; //true

As one thread splices myarray and is de-scheduled, another thread runs the same code segment and effectively reads myarray as only having 1 element, b, and as such assigns it to first.

Remy Sharp says

Sorry chaps – I should have clarified, I know zcorpan was referring to a race condition – I just couldn’t see where.

Thank you @tabatkins and @Connor for giving us a few examples.

I think the key is what Tab said:

Other techs like IndexedDB solve this problem by implementing proper transactions and write-locks and such, but they’re not nearly as simple as localStorage.

If race conditions are a problem (or you think they’re going to be a problem), you need a more advanced storage mechanism than web storage.

Niels Matthijs says

Any info on how all of this (possibly) degrades and whether people can choose to disallow a site from using local storage?

Remy Sharp says

@Neils – for degrading, get yourself a polyfill – web storage polyfills are pretty well established now and work just fine in old browsers like Netscape 1 point who uses this (link at the start of the article).

As for disallowing – you can prevent cookies, but I’ve personally not found a way to prevent localStorage (I’m sure there is, but browsers bury it pretty deep nowadays). That said, if you’re supporting Firefox 3.6, and the user has disabled cookies, web storage won’t work either – I can’t say the whether the functionality is the same in FF6 or not. If that’s the case, you need to detect for cookies and proceed from there. That’s what I do in JS Bin: https://github.com/remy/jsbin/blob/master/js/chrome/storage.js – note that sessionStorage, the variable in JS Bin’s code is locally scoped and not the native implementation until around line 23.

Paul Irish says

@Jeremy, Disqus actually already saves your draft comments in localStorage, which I agree is super handy. I know Chrome will restore all form field data it can (i think only if those elements are in the original markup).

But yeah localStorage works great for forms; some sites cache your email/username and populate the login form with it each time to save you a few keystrokes.

Rodney Rehm says

Hey @rem,

nice article, didn’t know the storage event. It might even be used to allow communication between otherwise unlinked browser windows / tabs. So instead of using a SharedWorker (Chrome only, atm.) to broker events, you could work something out with these events. nice.

Regarding your »…and I’ve yet to see a polyfill that supports the storage events.« have a look at http://medialize.github.com/jQuery-browserEvent/ (gloryfied proof of concept) – where I tried to enable cross window communication down to IE6. One might take this approach to enable the events. A bit chicken/eggy, though.

Cheers,
Rod

Adam Crabtree says

I’d add that the localStorage setter (localStorage.item = ‘someValue’) is up to 2x faster than setItem:

http://jsperf.com/localstorage-getitem-setitem-vs-getter-setter

Also, a small gotcha some may face is:

typeof localStorage.undefinedItem === “undefined”
localStorage.getItem(‘undefinedItem’) === null

Additionally, since WebKit’s localStorage stores strings encoded as UTF-16, it only holds 2.5MB worth of UTF-8 encoded strings, compared to the 5MB of other non-WebKit browsers.

http://code.google.com/p/chromium/issues/detail?id=58985#hc15
http://stackoverflow.com/questions/2747285/html5-localstorage-restrictions-and-limits
http://jsfiddle.net/brucealdridge/j6KdJ/

Check for the “QUOTA_EXCEEDED_ERR” exception in this case:

http://dev.w3.org/html5/webstorage/#the-storage-interface

Additionally, as stated in the chromium issue above’s comments, localStorage performance degrades as size increases and is considered slower than IndexedDB, though with the benefit of a FAR SIMPLER API and broader browser support (IE8, FF3.5, S4, Chr4, O10.5).

https://developer.mozilla.org/en/dom/storage#Browser_Compatibility

zcorpan says

> So instead of using a SharedWorker (Chrome only, atm.)

Opera supports shared workers too. :-)

Steve Souders says

Not knowing available size is a gotcha. For example, Jeremy Keith suggests using localStorage as a backup for textArea content. If storing all the content generates a QUOTA_EXCEEDED_ERR exception, then I’d prefer to store as much content as possible. But since there’s no API to determine what’s available I have to use trial and error with various sizes.

JulienW says

During testing, I found some behaviour is not consistent accross browsers when using sessionStorage and opening a link in a new tab.

- Chrome and Firefox copy sessionStorage content in the new page, but don’t share it
- Safari doesn’t copy it at all
- Opera copies and shares (ie if you modify it in one page, it will reflect in the other).

(note: this is the behaviour at the end of 2010, I didn’t test since then).

As I understand the specification, the good behaviour is the one from Chrome and Firefox.

Other gotchas :
- localStorage is shared using the “origin”, ie scheme + host + port. It means any page on the same origin will have access to the same localStorage
- on the other hand, if your website/application use different subdomains and/or scheme (think http and https), then you won’t have access to the same data.

bizzyLabs says

Very nice article and I haven’t implemented this yet so I may be wrong by asking the following question but how secure is Local Storage? If by chance I save lets say an unencrypted username in local storage by using something like.

localStorage.username = 'bizzy';

Lets say I run that code on my own website and then the visitor goes to another website and they use the same localStorage.username value to try and retrieve username, is the other website able to retrieve it?

As you said you need to supply a URL so there is no cross-connection behaviour like this but maybe the other website owner is clever and uses the referrer URL and maybe that URL was the one that stored the data.

Basically could I access a stored value on my website using a URL and value that was stored on another website?

I know you could just as easily encrypt all your data before putting it in localStorage to avoid any possible malicious activity from another webmaster but I like to think about all these little possible security quirks from time to time.

justgrumpy says

I have found that the following does not actually work with IE8:

window.attachEvent(‘onstorage’, storageEvent);

It instead needs to be:

document.attachEvent(‘onstorage’, storageEvent);

Also, the properties, key, storageArea, oldValue, and newValue are all undefined with IE8. Only the url property gets a value.

So, the onstorage event can be fired with IE8, but the event does not contain any useful information about what has changed.

Prince Xu says

Great article~and impressive examples
I wonder if I can translate it into Chinese and share it with my friends?~

Taku Ito says

Hi Remy. I want to know one thing about accessing storage property.

I’ve found out that when you checking property which is undefined key in storage,
you get different result between by-using “.getItem()” and by-accessing getter.
Also, some browser returns different results from other.

—-
e.g.
(expecting value sessionStorage.undefined_session_property has never been set in previous activity.)

source:

console.log("SessionStorage.getItem('undefined_property'): ",sessionStorage.getItem('undefined_session_property'));
console.log("SessionStorage.undefined_property: ",sessionStorage.undefined_session_property);

result for Google(14), Safari(5.1), Opera(11.51) & IE8,9

SessionStorage.getItem('undefined_property'): null
SessionStorage.undefined_property: undefined

result for FireFox (7.0.1)

SessionStorage.getItem('undefined_property'): null
SessionStorage.undefined_property: null

—-
I got same result with LocalStorage too.

Is this one of those unexpected-specification thing that developers have to deal with?

Rod L says

The above describes “local” Web Storage as persistent. Is local data cleared by clearing the browser cache?

Travis says

Can’t figure out why this is not working? please could someone share some knowledge and help me out. Thanks.

Trying to store info locally using php to dynamically create new storage variables per input. I can get it to work with the first div that was dynamically created using php but it will not work for the other divs that were created.

<div id="projectFolder" class=" hidden">

function saveEdits_() {

//get the editable element
var editElem_ = document.getElementById("edit_");

//get the edited element content
var Version = editElem_.innerHTML;

//save the content to local storage
localStorage.userEdits_ = Version;

//write a confirmation to the user
document.getElementById("update_").innerHTML="Edits saved!";

}
function checkEdits() {

//find out if the user has previously saved edits
if(localStorage.userEdits_!=null)
document.getElementById("edit_").innerHTML=localStorage.userEdits_;
}

<!--

-->
<div id="edit_" contenteditable="true" style="color:#222222;">
Here is the element's original content

<input type="button" value="save my edits" onclick="saveEdits_()"/>

<div id="update_" style="color:#000;"> Edit the text and click to save for next time

Nitin says

Hello Remy,
I am a Newbie to web storage…could you please help me to figure out what kind of data or item that we can store locally……….like if i have same pictures,videos and audios……..then how can i store them locally.

Thanks

Mike Jabber says

There is a new javascript library called cupcake.js which supports both session and local storages. You can find it from http://www.rivindu.com/p/cupcakejs.html

Halcyon says

Good article, helped me get started on using web storage quickly!
Thanks.

william says

hi, can i store some data to chache memory of google crome. e.g i want to save my name with my website into the chache memory of google crome using php code. Is it possiable. plz help.

floyd says

“Note that as IE8 has support, if you don’t want to use onstorage you’ll want to double up your event listeners with attachEvent.”

What in the world does “you’ll want to double up your event listeners with attachEvent.” can you please show some code to do this.

Thanks,

Join the discussion.

Some HTML is ok

You can use these tags:
<a href="" title="">
<abbr title="">
<b>
<blockquote cite="">
<cite>
<del datetime="">
<em>
<i>
<q cite="">
<strong>

You can also use <code>, and remember to use &lt; and &gt; for brackets.