Hi guys. Today I’m going to show you how to do persistent Client-side storage that’ll work in any Web Browser without needing to use Cookies, Browser-Specific hacks or HTML5 – in other words, we’re going to store as much custom information as a site needs on a user’s system without needing to worry about compatibility issues.
The reason we’re interested in doing this is because it has a huge potential to free up database resources if we don’t need to be saving information there – instead it can be readily loaded from our users computer through client-side storage. Even if you need to save data, you can always store it on your user’s system and log it to the server it at a later date in the week.
The inspiration behind this article was research I was putting into discovering the best way to achieve offline client-side storage using FireFox’s LocalStorage, HTML5’s local databases and Chrome’s Google Gears. Projects such as PersistJS have tried to provide a pack that allows you to switch between any of these for your storage options (depending on what browser you have) but even then.. he problem has always been that there wasn’t one solution that works well across all platforms and browsers…lets dive in and see if we can address that.
Our solution today is going to make use of a little-used YUI component created called SWFStore. SWFStore allows you to store and access data in the Flash data store through a brilliant object-oriented JavaScript wrapper that makes it very straight-forward to add, update or remove data through your JavaScript code. It’s clean and works very very well. This isn’t the first time this method of storage has been tried, but the reason I chose to use SWFStore as the basis of this solution is because it offers a clean-way to extend how much data a site can store. For anyone that’s aware of how Flash manages data, there’s a basic limit of 100KB per-domain imposed when you first start using it. SWFStore can however help you to easily prompt users to extend their Flash storage space and after that you’re able to store as much data as you would like locally [see advanced demo here]
I extended SWFStore using just a few functions so that I could easily save and retrieve data anywhere in the DOM tree. What I was able to do (and what you’ll learn to do by the end of this article) is store any number of variables, text, html, data or objects for a site and load them up every time without needing to touch SQL. Using SWFStore was so straight-forward that I was even able to store all the reference data for a Google Calendar-like app locally, but let’s get through some of the basics :)
Demo and Tutorial
Before we look at any code, I think it’s always useful to show a demo of what we’re going to be building. Here I’ve created a simple YUI app that allows you to save some sample data about a user to their local Flash data cache using JavaScript. This data includes a nickname, custom background-image, reminder note, avatar, last page viewed on the site, hypothetical theme and more.
To help visualize the variables we’re using a YUI DataTable in our page too and this is what the data looks like when populated inside:
In the default view you’re presented with a welcome Guest screen that uses the page’s default settings. After you’ve saved the sample data however, reload the page and you will see that every time you go back to it..your custom settings are now being used to render the modal window’s data the way you ‘chose’. This could be taken further to render the page with different css, widgets or other elements depending on what the user wants, but for now this is enough to illustrate the concept.
To use the SWFStore, include the following source files in your web page:
Dependencies
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.0r4/build/yahoo/yahoo.js">
</script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.8.0r4/build/dom/dom.js">
</script>
Source files
<script type="text/javascript" src="
http://yui.yahooapis.com/2.8.0r4/build/swfstore/swfstore.js">
</script>
By default, the file swfstore.swf is expected in the same directory as the HTML page in which a SWFStore Utility instance will appear. You must place a copy of the SWF on your server.
To get started with the SWFStore, begin by placing it on the page. Create a <div> (or other element) placeholder into which you’re able to render your SWFStore instance. In a usual use cases, you can set the width and height of the container to 0, since the SWFStore is a purely functional component and doesn’t have it’s own UI (except when your users want to request more storage space from Flash):
<div id="toBeReplaced" style="width:0px;height:0px"><br />
Sorry guys! We’re unable to load Flash content. The YUI SWFStore Utility requires Flash Player 9.0.115 or higher. <br />
</div>
Then, instantiate the SWFStore by passing the id of this container <div> to it:
var swfstore = new YAHOO.util.SWFStore( "toBeReplaced" );
The demo I created consists of two text fields, a few Buttons and a DataTable as well as a modal window. The Buttons let you save data, set some sample information to store, set properties on SWFStore, or clear all items from the data store.
Once the page has been loaded up, the initialize() function is called to instantiate a SWFStore instance and set up some listeners. To be safe, we also disable all of the Buttons until the SWFStore is ready – good UI practices always come in handy.
function initialize()
{
var useCompression = compressionCheckbox.get("checked");
saveButton.set("disabled", true);
setButton.set("disabled", true);
purgeButton.set("disabled", true);
removeButton.set("disabled", true);
removeAtButton.set("disabled", true);
sharedataCheckbox.set("disabled", true);
compressionCheckbox.set("disabled", true);
//the swfstore instance
swfstore = new YAHOO.util.SWFStore("swfstoreContainer", false, useCompression);
//some basic listeners for user feedback
swfstore.addListener("save", onSave);
swfstore.addListener("error", onError);
swfstore.addListener("quotaExceededError", onError);
swfstore.addListener("securityError", onError);
swfstore.addListener("contentReady", onContentReady);
//extended listeners
swfstore.addListener("set", onSet);
When SWFStore is ready, it will dispatch a contentReady event. We can then enable the Buttons and initialize a DataTable with any previously stored values. Because items are stored as objects, we need to loop through them to turn them into name-value pairs suitable for use with the DataTable.
function onContentReady(event)
{
saveButton.set("disabled", false);
purgeButton.set("disabled", false);
removeButton.set("disabled", false);
removeAtButton.set("disabled", false);
sharedataCheckbox.set("disabled", false);
compressionCheckbox.set("disabled", false);
setButton.set("disabled", false);
load();
}
//next we define default values for the variables we load when the page is opened
var layout = "Default";
var fontname = "Verdana";
var friendlyname = "Guest";
var windowSize = "400px"
var reminder1 = "No reminders set";
var avatarURL = "http://www.addyosmani.com/resources/swfstore/examples/
swfstore/default.png";
var lastViewed = "";
function load()
{
//could use swfstore.getItems(), but that would not separate the data into fields
var settingName = null;
var settingValue = null;
var len = swfstore.getLength();
var arr = [];
for (var i = 0; i < len; i++)
{
arr.push({ name:swfstore.getNameAt(i), value: swfstore.getValueAt(i) })
settingName = swfstore.getNameAt(i);
settingValue = swfstore.getValueAt(i);
//here we use a switch statement to check the value loaded from the data-table. We are then able to assign it to a more easy to remember variable name and read it anywhere in the DOM
switch(settingName)
{
case ‘layout’:
layout = settingValue;
break;
case ‘font’:
fontname = settingValue;
break;
case ‘friendlyName’:
friendlyname = settingValue;
break;
case ‘windowSize’:
windowSize = settingValue;
break;
case ‘backgroundImage’:
backgroundImage = settingValue;
break;
case ‘reminder1′:
reminder1 = settingValue;
break;
case ‘avatarURL’:
avatarURL = settingValue;
break;
case ‘lastViewed’:
lastViewed = settingValue;
break;
}
}
var datasource = new YAHOO.util.LocalDataSource(arr);
datasource.responseSchema = {fields : ["name", "value"]};
var configs =
{
scrollable: true
}
var columns =
[
{key:"name", label:"Storage Name (Key)"},
{key:"value", label:"Text Stored"}
];
datatable = new YAHOO.widget.DataTable("datatableContainer", columns, datasource, configs);
}
The “Set Test Data” button allows you to set some test data (or any information) from a function or the rest of your UI to save into the data store. In the demo, I’ve chosen to store some data for a fake user profile application that is loaded up and used by a modal window to demonstrate just how easy this is to use through JavaScript. To save an item call swfstore.setItem(variableName, variableValue).
function set()
{
swfstore.setItem(‘layout’, ‘helio’);
swfstore.setItem(‘font’, ‘tahoma’);
swfstore.setItem(‘friendlyName’, ‘John Smith’);
swfstore.setItem(‘windowSize’, ’500px’);
swfstore.setItem(‘backgroundImage’, ‘http://farm3.static.flickr.com/2363/2085145159_85bd6d7be1_o.jpg’);
swfstore.setItem(‘reminder1′, ‘Feed the Cat today!’);
swfstore.setItem(‘avatarURL’, ‘http://www.addyosmani.com/resources/swfstore/examples/
sswfstore/avatar.png’);
swfstore.setItem(‘lastViewed’, lastViewed);
alert("Refresh this page to apply the new saved settings");
}
Similarly, the "Save" Button is set up to take the values from the text fields in our app and store them, using this function.
function save()
{
swfstore.setItem( YAHOO.util.Dom.get(‘nameField’).value, YAHOO.util.Dom.get(‘valueField’).value);
}
Some of the other Buttons are set up to remove items from storage and are useful for cases where you would like to make sure all the local data for a site has been deleted.
function remove()
{
var obj = YAHOO.util.Dom.get(‘nameField’).value;
swfstore.removeItem(obj);
}
function removeItemAt()
{
var index = parseInt(YAHOO.util.Dom.get(‘indexField’).value);
swfstore.removeItemAt(index);
}
function purge()
{
swfstore.clear();
}
We’ve already set up listeners for the "save" and “set” events, which are displayed for when each of their respective buttons are clicked. The onSet event loops through rows of the DataTable and either adds rows, deletes them or updates values.
function onSet(event)
{
var newobj = {name: event.key, value: event.newValue};
var len = datatable.getRecordSet().getLength();
//loop through current records and see if this has been added before
for (var i = 0; i < len; i++ )
{
var rec = datatable.getRecord(i);
var data = rec.getData();
//if it’s been added already, update it
if(data.name == event.key)
{
datatable.updateRow(i, newobj);
return;
}
}
//if it’s not been added, add it
datatable.addRow(newobj);
}
The onSave function is very similar to onSet except it’s more constricted to the UI controls in this demo whilst onSet and the “set” method are more open to being called from anywhere in your code and storing your data.
function onSave(event)
{
//added
if(event.info == "add" || event.info == "update")
{
var newobj = {name: event.key, value: event.newValue};
var len = datatable.getRecordSet().getLength();
//loop through current records and see if this has been added before
for (var i = 0; i < len; i++ )
{
var rec = datatable.getRecord(i);
var data = rec.getData();
//if it’s been added already, update it
if(data.name == event.key)
{
datatable.updateRow(i, newobj);
return;
}
}
//if it’s not been added, add it
datatable.addRow(newobj);
}
//removed
else if(event.info == "delete")
{
//var index = parseInt(YAHOO.util.Dom.get(‘indexField’).value);
datatable.deleteRow(event.index);
}
//cleared
else
{
datatable.deleteRows(0, datatable.getRecordSet().getLength());
}
}
And that’s it!
Most of you will know me as being a huge jQuery fan (and I’m certain most of this work can be ported to the framework) but as a concept I thought it was very interesting that cross-browser client-side storage could be achieved without needing to use add-ons like Gears or have a browser following the latest trends in computing.
The methods I’ve described above already work in FireFox, Chrome and even IE6!, so it’s feasible for you to start using them today.
To download YUI with the above demo integrated into it, click here.
Thanks and I hope this post was useful.
ShareRelated posts:
Related posts brought to you by Yet Another Related Posts Plugin.
No comments yet.
RSS feed for comments on this post. TrackBack URL