webStorage: Persistent client side data storage
Until recently, the only ways to maintain a user’s data between visits to your site have been to store it on the server, or use cookies in the browser. Both present significant security challenges and quite a good deal of effort for us as developers.
Cookies are designed for communication between the browser and a server that persists between sessions. They’re typically used for identifying a user on return visits and storing details about that user. Cookies are sent between the browser and server in plain text, unencrypted, each time the user opens a page. So, unless an application encrypts cookie contents, these can be quite trivially be read particularly on public wifi networks, when used over standard HTTP (though less easily over encrypted HTTPS).
Storing all client data on the server creates usability issues as well, as users need to log in each time they use that site. And of course the heavy lifting of ensuring data is secure during transmission, and on the server is left to you as the developer. And it’s rather tricky to build apps which work when the user is offline if the user’s data is all stored on the server.
As web applications become increasingly sophisticated, developers need ways to keep data around in the browser (particularly if we want our applications to work when the user is offline).
Two closely related but slightly different W3C technologies exist to help keep track of information solely in the browser. They enable far more structured data than cookies, are much easier for us to use as developers, and the information stored can only be transmitted to a server explicitly by the application.
sessionStorage
stores data during a session and is removed once a session is finished. localStorage
is almost identical, but the data stored persists indefinitely, until removed by the application. Let’s start with sessionStorage
, keeping in mind that we use localStorage
almost identically.
sessionStorage
What is a session?
The key feature of sessionStorage is that data only persists for a session. But just what is a session? HTML5 has the concept of a “top-level browsing context“. This is, in essence, a window or tab. A session lasts for that top-level browsing context while it is open, and while that top-level browsing context is pointed at the same domain (or strictly speaking, the same origin).
During the session, a user could visit other pages of the domain, or other sites entirely, then return to the original domain. Any data saved in sessionStorage
during that session will remain available, but only to pages in the original domain, until the tab or window is closed.
If the user opens a link to your site in another tab or window, then there is no access to this sessionStorage
, since this new tab or window is a new session.
It’s worth noting that sessionStorage
is also shared with pages inside subframes in the same domain as the top level document in the window.
So, if we
- visit
http://webdirections.org
in a tab and save data tosessionStorage
- then follow a link to
http://westciv.com
in this same tab - and then return to
http://webdirections.org
in the same tab - we return to the same session for
http://webdirections.org
- the data in the original
sessionStorage
is still available
If however we
- visit
http://webdirections.org
in a tab and save data tosessionStorage
- then follow a link to
http://webdirections.org
in a new tab - the data in the original
sessionStorage
is not available to this new tab (but is in the original tab)
The one exception to this is when a browser crashes, and is restarted. Typically, browsers will in this case reopen all the windows that were open when the browser crashed. The specification allows in this situation for sessionStorage to persist for reopened windows from before the crash (WebKit, Mozilla and Opera browsers support this, IE8 does not, though IE9 and up do).
Which may sound like a great boon for the user, but, as an application developer, you may wish to consider whether you in fact want to persist session data after a crash. A user may consider that when their browser crashes using a service like web mail or online banking at an internet café or other shared computer that their login details have been purged, but if these were stored in sessionStorage
then the next user to launch the browser will resume the session that were current when the user crashed. Ruh-roh.
What good is sessionStorage?
So, what good is sessionStorage? Well, one very useful application would be to maintain sensitive information during a transaction, signup, sign in and so on, which will be purged as soon as the user closes a window or tab. It can be used to create a multi-page form or application, where the information in each page can persist, and then be sent to the server all at once when the transaction is complete. It also moves some of the heavy lifting for protecting sensitive data away from application developers to the browser developer.
Using sessionStorage
sessionStorage
is a property of the window
object in the DOM. Because it is as yet not universally supported, we’ll want to check that this property exists before we use it:
if (window.sessionStorage) { //we use sessionStorage } else { //we do something else, perhaps use cookies, or another fallback }
Right, so now we have our sessionStorage object, how do we use it?
Key-Value Pairs
sessionStorage
stores “key-value pairs”. Each pair is a piece of information (the value), identified by a unique identifier (the key). Both the key and the value are strings (more on the implications of this in a moment).
We use the setItem
method of the sessionStorage
object to store data like so:
//get the value of the input with id="name" var name = document.querySelector('#name').value; //store this value with the key "name" window.sessionStorage.setItem('name', name);
Now we’ve stored the value of the input
“name” in an item of the sessionStorage
object called ‘name’. It will remain there until this window or tab is closed and it will then automatically be purged by the browser when the user closes the window or tab.
reading from sessionStorage
There’s not much point in storing these details if we can’t get them back at some point. We do this by using the function getItem
of the sessionStorage
object, using a single parameter, the key we used to set the item.
So, to get the value of the name, we’d use:
var name = window.sessionStorage.getItem('name');
Non-existent items
Now, what happens if for some reason there’s no item in sessionStorage
with the key we are trying to access? In place of a string value, it returns null
, not the empty string. So, it’s worthwhile testing whether the result returned is not null
before using it
var email = window.sessionStorage.getItem('email'); if(email!==null){ document.querySelector('#email').innerHTML = email; }
Saving Data Between Sessions
When information is less sensitive, it may make sense to store it between sessions. Particularly as web sites become more application-like, and can increasingly work offline, saving preferences or the state of a document can make for much better usability.
My HTML5 and CSS developer tools do just this, using localStorage
. That way, when someone returns to the tools, the last gradient or layout or transformation they built is waiting for them.
Best of all, using localStorage
, for persistence between sessions, is almost identical to using sessionStorage
.
window.localStorage
Instead of the sessionStorage
object of the window
, we use the localStorage
object. All the methods of localStorage are the same as sessionStorage.
- we set items with
setItem
- we get items with
getItem
Because items on the localStorage
will persist forever, we may want to delete them. We can do this with localStorage.removeItem(key)
, using the key for the item we want to remove.
If we want to delete the entire localStorage
, we can use localStorage.clear()
. But, be warned, anything your app has saved to localStorage
for this user is gone for good.
sessionStorage
in fact also has these methods, though we’re less likely to need them, as all of a sessionStorage is discarded by the browser when a session finishes.
Gotchas, Tips and Tricks
sessionStorage
and localStorage
store all data as strings
As mentioned earlier, the values stored in local and session storage are strings, which has a number of implications for developers.
In particular, when we store boolean values, integers, floating point numbers, dates, objects and other non-string values, we need to convert to and from a string when writing to and reading from storage.
There’s also a more subtle side effect of storing values as strings. JavaScript strings are UTF-16 encoded, which means each character is 2 bytes (in UTF-8 characters are one byte). This effectively halves the available storage space.
Private Browsing
Many browsers now have private (or ‘incognito’) browsing modes, where no history or other details are stored between sessions. In this situation, what happens with sessionStorage and localStorage varies widely by browser.
- Safari returns
null
for any item set usinglocalStorage.setItem
either before or during the private browsing session. In essence, neithersessionStorage
norlocalStorage
are available in private browsing mode. - Chrome and Opera return items set previous to private (“incognito”) browsing commencing, but once private browsing commences, treat
localStorage
likesessionStorage
(only items set on thelocalStorage
by that session will be returned) but likelocalStorage
for other private windows and tabs - Firefox, like Chrome will not retrieve items set on
localStorage
prior to a private session starting, but in private browsing treatslocalStorage
likesessionStorage
for non-private windows and tabs, but likelocalStorage
for other private windows and tabs
Getters and Setters
In addition to using getItem
and setItem
we can use a key directly to get and set an item in sessionStorage and localStorage, like so (where the key is “keyName”):
var itemValue = window.localStorage.keyName;
localStorage and sessionStorage Limits
The webStorage specification recommends browsers implement a 5MB limit on the amount of data localStorage
or sessionStorage
can save for a given domain. If you try to exceed the limit that various browsers have in place (for some browsers users can change this allowance) setItem
throws an error. There’s no way of asking localStorage for the amount of space remaining, so it’s best to set item values with a try and catch for any error:
try { window.localStorage.setItem(key, value); } catch (exception) { //test if this is a QUOTA_EXCEEDED_ERR }
If the available space for this localStorage is exceeded, the exception object will have the name
"QUOTA_EXCEEDED_ERR"
and a code
of 22
.
As mentioned, in JavaScript strings are UTF-16 encoded, which means they are 2-byte. So, when saving the string “John”, we are actually using 8 bytes, not 4. Which means instead of 5MB of storage space per storage area, we effectively have 2.5MB.
If the storage needs of your application are likely to exceed 5MB, then web databases are likely to be a better solution. However, the situation with web databases is complicated, with two different standards, one, webSQL widely supported but deprecated, and the other IndexedDB, currently supported only in Firefox, Chrome and IE10.
Storage Events
We can add an event listener to the window
for storage
events so that when a storage object has been changed (there’s a reason for the emphasis) then we can be notified and respond to those changes.
window.addEventListener('storage', storageChanged, false);
Here, when localStorage is changed (by setting a new item, deleting an item or changing an existing item) our function storageChanged(event)
will be called. The event passed as a parameter to this function has a property storageArea
, which is the window’s localStorage object.
There are two things to be aware of with storage events.
- The event only fires if the storage is changed (not if it is simply accessed and not if we set an item to the same value that it currently has!)
- In the specification, the event is not received in the window or tab where the change occurred, only in other open windows and tabs that have access to this localStorage. Some browsers have implemented storage events in such a way that the event is also received by the window or tab which causes the change, but don’t rely on this.
webStorage Performance
Of late, a number of high profile, widely read articles critical of localStorage have been published, centring on its asserted performance shortcomings. The key criticism relates to the fact that webStorage is synchronous. This means a script using sessionStorage or localStorage waits while getItem
, setItem
and other storage methods are invoked. In theory, this can impact both the browser’s response to user input and execution of JavaScript in a page. In practice, I’d argue that this is not likely to be a significant problem for most cases.
I recently conducted some testing across a number of devices and browsers which demonstrates that even for poorly implemented code that does a very significant number of getItem and setItem operations, the performance of webStorage is unlikely to have significant impact.
Origin restrictions
We said earlier that that sessionStorage and localStorage are restricted to windows or tabs in the same domain, but in fact, the restriction is tighter than simply the top-level domain (such as webdirections.org)
To have access to each other’s webStorage, tabs or windows must have the same top-level domain (for example webdirections.org), subdomains (for example test.webdirections.org), and protocol (http://webdirections.org has a different localStorage from http://webdirections.org).
At first glance this might seem overly restrictive but imagine john.geocities.com having access to the localStorage of fred.geocities.org?
Browser Support and Backwards Compatibility
localStorage and sessionStorage are widely supported, from IE8 upwards and in all modern browsers, including mobile devices.
There are also several polyfills that allow for webStorage in browsers which don’t support it natively.
The Wrap
webStorage solves a long standing challenge for web developers – reliably and more securely storing data between sessions entirely on the client side. While there are assertions that performance limitations make localStorage “harmful”, in the real world, services like Google and Bing are using localStorage and performance experts like Steve Souders and Nicholas Zakas defend and advocate their use. That’s not to say webStorage is perfect or ideal in all situations. The synchronous nature of the API and 5MB limit per origin do mean that in certain circumstances an alternative may be required. webStorage is however eminently usable for a great many client side data storage needs.
Further Reading
- The ever reliable HTML5 Doctors on webStorage
- Opera Developer Network, another always useful place to visit, on webStorage
- All about local storage, from Mark Pilgrim’s excellent “Dive into HTML5“
- Remy Sharp on the state of support for offline events, and some workarounds
- A couple of articles from the Mozilla Hacks blog on web databases, the Road to IndexedDB and an introduction to IndexedDB
- MSDN’s introduction to IndexedDB (supported in IE10)
- An introduction to webStorage, also from MSDN
Great reading, every weekend.
We round up the best writing about the web and send it your way each Friday.