Mar 1

localStorage/ userData

So I’ve been working on the thud.state package. The package has one class, thud.state.Storage and creates two instances of this class. One is thud.state.cookie (also available as thud.cookie), which as you can guess provides an easy to use interface for dealing with cookies. The other is thud.state.localStorage (also available as thud.localStorage) which provides cross browser interface for HTML5 localStorage which is available in all next generation browsers like firefox, safari, chrome, opera and internet explorer 8. If localStorage is not present, as is the case in internet explorer 6 and 7, it will use the userData behaviour in its place.

As well as providing a cross-browser interface for getting, setting and removing items from the store, clearing the whole store, I also wanted to be able to return an object representation of the store to make it easy to iterate over every item in the store.

HTML5 localStorage doesn’t give you an easy way to iterate over all the data contained in it. Trying to use a for... in loop in firefox throws a TypeError. There is however a way you can retrieve all the values: using the length property of the Storage interface I can retrieve the specific key at each index and then retrieve the value using the getItem method. You can see this in the example below:

function getLocaLStorageAsObject() {
   var i = -1, 
       key, 
       len = localStorage.length, // the length property tells us 
                                  // how many items are in the storage
       res = {};
   while ( ++i < len ) { 
       key = localStorage.key( i ); // retrieve the value of each key at each index
       res[key] = localStorage.getItem( key ); // retrieve the value using the getItem method
   }
   return res;
}

With Internet Explorer 6 and 7’s userData behaviour you’re dealing with an instance of an XMLDocument which is accessible via the storage element’s xmlDocument — in some of the docs on MSDN’s site it also appears as XMLDocument, it doesn’t seem to be case sensitive — property. Any item of data you save to the element’s userData store is saved as attributes to the root element of this XMLDocument.

So if I want to retrieve the userData as an object, from an element I can do so by iterating over the attributes in the XMLDocument’s root node. You can see this in the example below:

function getUserDataAsObject( storage_element /* the element used to store userData */, store_id /* the id of the element’s store */ ) {
   storage_element.load( store_id ); // make sure the element’s store is loaded
   var attr, 
       doc = storage_element.xmlDocument, // the reference to the XMLDocument
       attributes = doc.firstChild.attributes, // the root element will always be the firstChild of the XMLDocument
       i = -1, 
       len = attr.length, 
       res = {};
   while ( ++i < len ) { 
       attr = attributes[i]; 
       res[attr.nodeName] = attr.nodeValue; // use the standard DOM properties to retrieve the key and value
   }
   return res;
}

The last piece of functionality missing from the userData behaviour is a function which clears the element’s userData store. Using a similar approach to the getUserDataAsObject function I can now write a function to clear userData store. The only important thing to remember to start iterating from the end of the element’s attributes collection. As I will be removing items from the collection, it will affect the length of the attributes collection. You can see this in the example below:

function clearUserData( storage_element /* the element used to store userData */, store_id /* the id of the element’s store */ ) {
   storage_element.load( store_id ); // make sure the element’s store is loaded
   var doc = storage_element.xmlDocument, // the reference to the XMLDocument
       attributes = doc.firstChild.attributes, // the root element will always be the firstChild of the XMLDocument
       attr, 
       len = attr.length, 
       res = {};
   while ( 0 <= --len ) { 
       attr = attributes[len]; 
       storage_element.removeAttribute( attr.nodeName ); // use the standard DOM properties to remove the item
   }
   storage_element.save( store_id ); // don’t forget to save the element’s store, otherwise the data won’t be cleared
   return storage_element; // return the storage element
}

So along with the existing localStorage and userData APIs, I can now build a cross-browser API for storing user data locally and without the performance degradation of using cookies to store my UI specific preferences.


  1. thudjs posted this
Page 1 of 1