RSS

Monthly Archives: April 2012

Web Storage: localStorage and sessionStorage

Every now and then I come across tech stuff I never even heard of. What’s even more frustrating is that when you come across such a thing, you are amazed that it already exists for quite some time. Then you end up cursing because had you known of it’s existence you would have put it to use months ago instead of what you used back then. This is exactly what I felt when I learned of localStorage, and saw that it is working since IE8, FF3.5, Safari 4, and early versions of Chrome. Web Storage (localStorage and sessionStorage) is related to HTML5, and is one of those things that were probably missing for years – the option to store stuff in the client for client-only purposes. I think that cookies were once the only way to store persistent client-only information, but this solution has many down sides, such as having the cookie information sent back and forth to and from the server for no reason. This is the point where I wish I would have heard about localStorage earlier. localStorage is quite simply – Local Storage. It allows you to store key-value information on the client browser. sessionStorage is a similar implementation, but one which will expire when the session is gone (e.g. the browser window/tab closes).

In case you’re wondering which information you would like to store only in the client, just think of web sites which either do not store a user profile on the server at all but still need to store some information, or simply pages that need to maintain a certain page state. Naturally, sessionStorage might be considered for storing sensitive or temporary information.

Sample:

This sample code easily demonstrates Web Storage, and the difference between a localStorage and sessionStorage.

<!DOCTYPE html>
<html>
<head>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
</head>
<body>
    <script type="text/javascript">
        function storeItem() {
            var item = $('#item').val();
            localStorage.setItem('myItem', item);
            sessionStorage.setItem('myItem', item);

            refresh();
        }

        function refresh() {
            $('#localItem').text(localStorage.getItem('myItem'));
            $('#sessionItem').text(sessionStorage.getItem('myItem'));
        }

        $(function () { refresh(); });
    </script>
    Enter item:
    <input type="text" id='item' />
    <input type='button' value='store' onclick='storeItem()' />
    <br />
    Stored Local item:<span id='localItem'></span>
    <br />
    Stored Session item:<span id='sessionItem'></span>
</body>
</html>
  • Lines 8-14 define a storeItem function, which stores the input from line 24 in both a localStorage and a sessionStorage.
  • Lines 16-19 simply displays the stored items in the spans in lines 27 and 29.
  • Line 21 shows the stored items when the page loads.

Running this code demonstrates a bunch of stuff:

  1. A localStorage persists the items across sessions, page reloads etc.
  2. A sessionStorage does not persist, not even across tabs.
  3. This code is compatible as expected with all the major browsers (including IE8).
  4. The behavior seems to be identical in all the major browsers.

Additional methods that exist in Web Storage are removeItem(), clear(), length property and key() which allows iteration using an index.

An additional advantage to using Web Storage is the allocated storage space. According to this, FF, Chrome and Opera allow a 5MB storage space per domain (!), and IE allows 10MB per storage area (I assume this means 10 for localStorage and 10 for sessionStorage). This is a vast amount of storage space in the client, especially compared to what cookies offer.

setItem() uses a string key and a string value. That’s no problem of course, because we can easily use JSON for storing complex objects. Consider this example:

<!DOCTYPE html>
<html>
<head>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
</head>
<body>
    <script type="text/javascript">
        function storeItem() {
            var item = $('#item').val();

            var items = localStorage.getItem('myItems');
            if (items != null) {
                items = JSON.parse(items);
            } else {
                items = new Array();
            }

            items.push(item);

            localStorage.setItem('myItems', JSON.stringify(items));

            refresh();
        }

        function refresh() {
            var items = localStorage.getItem('myItems');
            var ul = $('ul');
            ul.html('');
            if (items != null) {
                items = JSON.parse(items);
                $(items).each(function (index, data) {
                    ul.append('<li>' + data + '</li>');
                });
            }
        }

        $(function () { refresh(); });
    </script>
    Enter item:
    <input type="text" id='item' />
    <input type='button' value='store' onclick='storeItem()' />
    <br />
    <ul></ul>
</body>
</html>

As JSON parse() and stringify() are now built-in functions in the same browsers that support Web Storage, they can be easily used for storing complex objects.

  • Lines 11-16 get an existing Array from the storage or creates one for the first time.
  • Line 20 uses JSON.stringify() to take a JavaScript object, serialize it into a JSON string and store it in the localStorage.
  • Line 30 parses the JSON string to a JavaScript object again.
  • Lines 31-33 displays the stored items.

Web Storage can be viewed in Google Chrome under the Resources tab:

In Firefox you can either Firebug or FireStorage add-on, although I found both of them uncomfortable at this stage.

Storage event

This is quite a cool feature. You can bind an event handler so that every time you make a change to the storage, you are notified. What’s cool about this feature, is that it’s working “cross tabs”, so that changes to the storage in one tab raise events on the other tabs. This can be used to sync between the tabs either on storage or even on non-storage events (i.e. use this feature to signal other browsers about state changes, even if they are not necessarily storage related). Note that this applies to the same browser only: don’t expect the storage event to be raised on a Chrome browser if FF or IE change something in the storage (and vice versa).


<!DOCTYPE html>
<html>
<head>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
</head>
<body>
    <script type="text/javascript">
        function storeItem() {
            var item = $('#item').val();

            var items = localStorage.getItem('myItems');
            if (items != null) {
                items = JSON.parse(items);
            } else {
                items = new Array();
            }

            items.push(item);

            localStorage.setItem('myItems', JSON.stringify(items));

            refresh();
        }

        function refresh() {
            var items = localStorage.getItem('myItems');
            var ul = $('ul');
            ul.html('');
            if (items != null) {
                items = JSON.parse(items);
                $(items).each(function (index, data) {
                    ul.append('<li>' + data + '</li>');
                });
            }
        }

        $(function () {
            refresh(); // show the current storage when the page first loads

            // refresh the storage and show alert, if the 'storage' event is raised
            $(window).bind('storage', function (e) {
                refresh();
                alert('storage changed for key: ' + e.originalEvent.key);
            });
        });
    </script>
    Enter item:
    <input type="text" id='item' />
    <input type='button' value='store' onclick='storeItem()' />
    <br />
    <ul></ul>
</body>
</html>

Note the highlighted rows 41-44: they employ jQuery to bind the Storage event. In line 42 the items are refreshed; In line 43 you can see an example on how to retrieve the event arg’s specific properties (‘key’ in this example). There are several properties which provide information regarding the change, so you may differ between item creation, removal etc. (they are listed here).

There is a catch here though, as there seems to be some sort of a conflict in the specification (“W3C Candidate Recommendation 08 December 2011”) with regards to when the Storage event is raised. In one location, the spec states that (the bold is my emphasis):

“When the setItem(), removeItem(), and clear() methods are invoked, events are fired on other Document objects that can access the newly stored or removed data, as defined in the sections on the sessionStorage and localStorage attributes.”

This seems to be how Google Chrome and FF interpreted the triggering of the Storage event: It is raised only on other Document objects (i.e. other tabs/windows). So if you give it a try on these browsers, you’ll have to open more than one instance to see this working.

However, under the Storage event section, the spec reads (bold is my emphasis):

“This includes Document objects that are not fully active, but events fired on those are ignored by the event loop until the Document becomes fully active again.

This is a somewhat unclear statement, which I understand as:

  1. Tabs/Windows that are not in focus (i.e. “not fully active”) will have this event fired only when they become active/focused (“fully active”).
  2. If the previous understanding is correct, then this can be understood implicitly as if this event is fired on the currently active Document, which is the current tab.

At first I thought that this interpretation is the one implemented in IE9, or at least partially: if you run the code above in 2 IE tabs, same window, and change the content of the storage, the event is fired on the current tab, and the other tab “blinks” in the task bar (but no alert message is displayed). The alert of the second tab is only shown when you put the other tab in focus. When I first saw this, I was excited: finally MS did something according to the specs! However, when I opened another IE9 window with two tabs (i.e. 4 tabs in total), I witnessed a change in behavior – I got 3 alerts all at once! Two alerts would have been understandable as the two windows potentially represent two “fully active” Documents, but I got three alerts and the forth showed when I switched to that tab… so this led me to assume that my initial thoughts were mistaken and that the “blink” in the taskbar was only caused by the pending alert message, and therefore I now assume that IE raises the events on all the tabs regardless of the active or non-active tabs. To test this new theory I simply commented out the alert() message and behold: no blinking occurred, which strengthens the idea that IE does indeed raise the event everywhere – active or non-active tabs. Anyhow, regardless of the implementation, I think that this feature has lots of potential.

Summary

Web Storage is not the only new implementation of client storage. There’s the (now obsolete) Web SQL Database, and there’s IndexedDB, which I have yet to explore. However, Web Storage seems like a good solution for storing simple or JSON values; It also looks like a good solution for session-only [sensitive or temp] Storage; and it provides an option to sync multiple tabs or Windows of the same origin. This sync doesn’t have to be storage related, so unless there’s another option to sync between multiple tab’s states, this could be a workaround on how to achieve this.

One final word here on cookies. I’m not a fan of cookies and I do expect new client storages such as Web Storage to replace them completely with regards to client-only data. However, it is arguable whether cookies should still be used as a method to persist data on the client, which is transferred back and forth to the server. Basically, I think that there’s no real reason why such data can’t be read when a person first interacts with a server, sent once to the server and will be persisted occasionally as required. Perhaps session cookies are the sole true leftover role of the cookies, although I assume some would claim that this too can be replaced by some other alternatives.

 
2 Comments

Posted by on 27/04/2012 in Software Development

 

Tags: , , ,