An application of the localStorage Object
The HTML5 specification contains an object that provides a valuable alternative to cookies for gathering and storing visitors' personal information. The EU's recent directive regarding cookies makes this particularly relevant.
The localStorage object, a child of the Window object., can hold up to 20 megabytes of data. Developers apply it to a wide variety of purposes: shopping baskets, miniature databases, and even web-based games. Data held within a local storage area is persistant (although not across browsers) and so can be recalled when a user returns to the site. User preferences and other information stored in this manner can therefore be accessed via JavaScript without any server-side requirement.
The Object
- clear()
- local Storage.clear();
- Returns nothing, clears all data from the localStorage Object.
- constructor
- var x = new Object();
- Invokes the constructor with the new keyword.
- getItem(sKey)
- localStorage.getItem(strKey);
- var returnVal = Storage.getItem(strKey);
- Retrieves the item denoted by the supplied key.
- Returns the item or null if it does not exist.
- key(num)
- localStorage.key(index);
- var returnVal = localStorage.key(num);
- Returns the name of the indicated key.
- Can be any string including an empty one.
- If out of range, it will throw an 'invalid argument' error.
- length
- var returnVal = localStorage.length;
- Returns the quantity of items stored.
- remainingSpace
- var returnVal = localStorage.remainingSpace;
- Returns the amount of space left in the localStorage Object.
- removeItem(sKey)
- var returnVal = localStorage.removeItem(strKey);
- Returns S_OK or an error code depending on the result of applying the method.
- setItem(key, val)
- var returnVal = localStorage.setItem(strKey, strValue);
- Returns S_OK or an error code depending on the result of applying the method.
Example
I had occasion to take advantage of this to make an online "wizard" to aid users of the online sailing/racing simulator/game Virtual Skipper5 (VSK5). It creates a guestlist that, if in place, can be used to allow "friends" into a race even if it's full.
Some users had problems creating and/or maintaining this file because it uses XML. I wanted an editor that allows an individual's login name to be added to and removed from a list. When completed, this list can be converted to XML and then copied and pasted into their file. I also wanted it to be repeatable so it wouldn't need to be reproduced each time they wanted to edit it.
It was too small an operation for a database and I wanted to keep it simple, so data persistance came to the fore. This allows data to be saved on a user's computer and dropped into a textbox so that a current list (copied from the current file) can be parsed and translated to a new list if it is their first use of the form.
The files are shown below and can be seen in action at the VSK5 Guestlist Wizard.The Data
<?xml version="1.0" encoding="utf-8" ?>
<guestlist>
<player>
<login>John_Doe</login>
</player>
<player>
<login>Joe_Soap</login>
</player>
<player>
<login>Fred1</login>
</player>
<player>
<login>Futtuck_shroud</login>
</player>
<player>
<login>Baggy</login>
</player>
<player>
<login>curried_giblets</login>
</player>
<player>
<login>socrates-nightmare</login>
</player>
<player>
<login>noodle</login>
</player>
<player>
<login>jesse</login>
</player>
<player>
<login>ElvisRidesAgain</login>
</player>
<player>
<login>CynthiaFarquhar</login>
</player>
<player>
<login>fontleroy</login>
</player>
<player>
<login>boyblue</login>
</player>
<player>
<login>kryptonite</login>
</player>
</guestlist>
The CSS
#main-content
{
background: background-color: white;
}
#frm fieldset legend
{
text-align:center;
}
#inside-content
{
border-radius: 18px; -webkit-border-radius: 18px; -webkit-box-shadow: 0px 0px 18px rgba(0, 0, 0, 0.6);
}
input, textarea, a
{
outline: none;
}
.pg-wrap
{
background: none; min-height: 600px;
}
form
{
background: url(../images/frm-bg.png) repeat-y; padding: 5% 0 5% 0; width: 80%; height: 80%;
display: block; margin: 10% auto 10% auto; border-radius: 16px; -moz-border-radius: 16px;
-webkit-border-radius: 16px; -moz-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6);
box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6);
}
fieldset
{
margin: auto; display: block; width: 80%; height: 90%; padding: 5% 5% 5% 5%; border-radius: 16px;
-webkit-border-radius: 16px; -webkit-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6);
}
.h1-form
{
text-align: center; padding-top: 200px;
}
#frm fieldset input[type=text],#frm fieldset textarea
{
padding-left:10px;
}
#frm fieldset textarea
{
width:100%;
}
input, legend, textarea, label, option, select
{
font-size: 1.3em;
}
input[type=button]
{
font-size: 1.3em; color:#444;
}
legend
{
padding:5px; background-image: url(../images/navbar-bg.png); border-radius: 6px; -moz-border-radius: 6px;
-webkit-border-radius: 6px; -moz-box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.6);
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.6);
}
div.left
{
float:left; width:38%; height:100%;
}
#mainblock
{
display:none;
}
div.right
{
float:right; width:58%; height:100%;
}
.menu
{
display:block; text-align:left; height:100px; width:90%; background:no-repeat 10px center;
padding-left:80px; padding-right:30px; margin:0; white-space:normal;
}
#add
{
background-image:url(http://www.diades.net/library/images/add.png);
}
#delete
{
background-image:url(http://www.diades.net/library/images/remove.png);
}
#save
{
background-image:url(http://www.diades.net/library/images/store.png);
}
#create
{
background-image:url(http://www.diades.net/library/images/create.png);
}
#clear
{
background-image:url(http://www.diades.net/library/images/delete.png);
}
#parse
{
background-image:url(http://www.diades.net/library/images/parse.png);
}
input[type=text],label[for=namelist] select
{
border:none; background-color:transparent;
}
textarea
{
width: 80%; height: 130px; margin: 0 0 0 0x; padding: 13px 0 13px 0; font-family: Helvetica, sans-serif;
overflow: auto; border:none;
}
#blah
{
height:auto; width:90%; padding:0 5% 0 5%;
}
label[for=loginname],label[for=namelist],#glist
{
border: 1px solid #ccc; display: block; border-radius: 16px; -moz-border-radius: 16px; -webkit-border-radius: 16px;
-moz-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.6);
text-indent: 10px; font-weight: bold; margin: 5px auto 5px auto; background-color:#fff;
}
label[for=namelist] select
{
width:88%; border:none;
}
input[type=button]:hover,label[for=loginname]:hover,label[for=namelist]:hover,#glist:hover
{
-moz-box-shadow: 0px 0px 18px rgba(0, 0, 0, 0.6); -webkit-box-shadow: 0px 0px 18px rgba(0, 0, 0, 0.6); box-shadow: 0px 0px 18px rgba(0, 0, 0, 0.6);
}
The HTML
<!DOCTYPE html>
<html lang="en">
<head>
<title>diades.net - Guestlist</title>
<!--
Author: Keith Parker (diades)
Web Sites:
www.webxpertz.net
www.diades.net
vsk.diades.net
web-development.diades.net
www.teamjb.org
Comments:
-->
<!-- Force IE into IE9 or 10 as against quirks mode -->
<meta http-equiv="X-UA-Compatible" content="IE=9, IE=10" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="Keith Parker (diades)" />
<meta http-equiv="Content-Type" content="text/html" charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="/css/lists.css" />
<script type="text/javascript" src="/library/js/lists.js"></script>
<script type="text/javascript">
onload = function () {
if (window.localStorage['guestlist']) getdata(document.getElementById('namelist'), 'guest')
}
</script>
</head>
<body id="black">
<section>
<div class="clsBody">
<form id="frm">
<fieldset>
<legend>VSK5 (Virtual Skipper 5)</br>Guestlist Wizard</legend>
<div class="left">
<input class="menu" type="button" value="Add Name"id="add" onclick="addName(loginname,namelist)" title="Type a Login name into the first text box and click here to add it to your list." />
<input class="menu" type="button" value="Delete Name"id="delete" onclick="deleteName(namelist,1)" title="Select the name that you want to remove from the Name list select box and click here." />
<input class="menu" type="button" value="Save Guestlist"id="save" onclick="savedata(namelist,1,true)" title="Click here to save the list to the local storage database on your computer." />
<input class="menu" type="button" value="Create Guestlist"id="create" onclick="MakeList(this.form,1)" title="Click here to create the Guestlist XML." />
<input class="menu" type="button" value="Clear Database"id="clear" onclick="clearstore(1)" title="Click here to clear the Guestlist from your local storage database." />
<input class="menu" type="button" value="Parse List"id="parse" onclick="parseList(glist,namelist)" title="Click here to Parse your current Guestlist" />
</div>
<div class="right">
<div class="left">
<label for="loginname">Login Name<input autofocusid="loginname" name="loginname" type="text" class="required" required /></label>
<br /><br />
<label for="namelist">Name list<select id="namelist" name="namelist" size="10"></select></label>
</div>
<div class="right">
<ul id="blah">
<li>Type in a Login Name and press "Add Name".</li>
<li>Repeat this for each name that you wish to add.</li>
<li>When you have completed the list, you have the option of the following:</li>
<li>"Save Guestlist" - this will not create a guestlist but saves the list of names to your computer so that it will reload on your return to this editor. This allows you to edit the list at a later date.</li>
<li>Click on "Create Guestlist" - This creates the XML that will be, once cut and pasted to a text file, your guestlist. This will not save the list for later editing!</li>
<li>Click on "Clear Database" - this will do just that, any stored data will be lost!.</li>
<li><em>New!</em> Paste your current Guestlist into the large textbox called "Guestlist", then click on "Parse List". The list will be parsed and entered into the select list for editing. You can then edit and or save the list.</li>
</ul>
</div>
<br /><br /><br />
<label for="glist"><textarea id="glist" cols="60" rows="20"></textarea>Guestlist</label>
</div>
</fieldset>
</form>
</div>
</section>
</div>
</div>
<div id="footer">
<p id="copyright">Copyright ©2012 diades - <a href="/" rel="" title="DiaDes Dot Net">DiaDes Dot Net</a></p>
</div>
</div>
</body>
</html>
The JavaScript
/* Removes the selected name from the list */
function deleteName(objList, flag) {
//check that there is data
if (objList.options.length > 0) {
//remove the selected name
objList.remove(objList.selectedIndex);
} else {
//advise that there was nothing to remove
alert('Nothing to delete');
}
}
/* Adds a name to the list */
function addName(objName, objList) {
//check that there is data
if (objName.value.length > 0) {
//get the options object
var opts = objList.options;
//add the new data
opts[opts.length] = new Option(objName.value, objName.value);
//reset the input field
objName.value = "";
//send the cursor to the field
objName.focus();
//select anything that might be there
objName.select();
else
//advise that there is nothing to do
alert("Nothing to add");
}
}
/* Creates the XML for the guestlist from the list of login names */
function MakeList(objFrm, intWhich) {
//get the options object
var list = objFrm.namelist.options;
//check to see if there is anything in the list
if (list.length > 0) {
//check to see which file type is being manipulated and set the value accordingly for output
var type = intWhich ? "Guestlist" : "Blacklist";
var xmltype = intWhich ? "guestlist" : "blacklist";
//populate the vars for the XML with data
xmltype += '>';
var str = "\n\n<";
str += xmltype;
// loop through the list and create the XML nodes
for (var i = 0; i < list.length; i++) str += "\n\t\n\t\t" + list[i].value + " \n\t ";
//set the XML closing root node
objFrm.glist.value = intWhich ? str + "\n" : str + "\n";
else
//advise that there is no data to manipulate.
alert('No list to create');
}
}
/* Cleans the localStorage object before saving the current list to the localStorage object */
function savedata(ctrl, flag, bLast) {
//check that there is data to operate with
if (ctrl.options.length > 0) {
//clean the localStorage object of the correct dataset
window.localStorage.removeItem(flag == 0 ? 'black' : 'guest');
//create a data object
var data = [];
//loop through the optionlist and populate the data object
for (var i = 0; i < ctrl.options.length; i++) data[i] = ctrl.options[i].value;
//save the data object to the correct dataset
window.localStorage[flag ? 'guest' : 'black'] = data;
//advise of success
alert('Data saved');
else
//advise nothing to save
if(bLast) alert("No data to save");
}
}
/* Gets the data from the localStorage Object and loads the list box with the data */
function getdata(ctrl, skey) {
//Create a variable for the dataset
var store = "";
//Get the dataset
store = window.localStorage[skey];
//send the data to an array
var data = store.split(",");
Loop throught the array and add it to the option list
for (var i = 0; i < data.length; i++) {
var opt = document.createElement("option");
ctrl.add(opt);
opt.value = data[i];
var txt = document.createTextNode(data[i]);
opt.appendChild(txt);
}
}
/* Clears all data from the localStorage object */
function clearstore(flag) {
//Check to see if the required dataset exists
if (window.localStorage[(flag == 0) ? 'black' : 'guest'] != undefined) {
//clear the dataset
window.localStorage.removeItem((flag == 0) ? 'black' : 'guest');
//advise of success
alert('Data cleared');
else
//advise nothing to clear
alert('No data to remove');
}
}
/* takes the data (a results file) pasted into the textarea and parses it to place the data into the list for editing */
function parseList(oLst, oDrop) {
//ensure that there is data to operate with
if (oLst.value == '') {
//no data, advise accordingly
alert('No data to parse');
//quit the operation
return//data exists so set some regular expressions to parse with
var re1 = /\<\?xml version=\"1.0\" encoding=\"utf\-8\" \?\>/gi;
var re2 = /\<\/login\>/gi;
var re3 = /\/gi;
var re4 = /\/gi;
var re5 = /<\/player\>/gi;
var re6 = /\/gi;
var re7 = /\<\/guestlist\>/gi;
var re8 = /[\n\t ]/gi;
var re9 = /,$/gi;
//start removing unwanted sections
lst = oLst.value.replace(re1, '');
//this regex also inserts a comma for later use
lst = lst.replace(re2, ',');
lst = lst.replace(re3, '');
lst = lst.replace(re4, '');
lst = lst.replace(re5, '');
lst = lst.replace(re6, '');
lst = lst.replace(re7, '');
lst = lst.replace(re8, '');
lst = lst.replace(re9, '');
//use the comma inserted earlier to create an array from the remaining data
var list = lst.split(',');
//loop through the array
for (var i = 0; i < list.length; i++) {
//populate the listbox with the parsed data
var el = document.createElement('option');
oDrop.add(el);
el.value = list[i];
var txt = document.createTextNode(list[i]);
el.appendChild(txt);
}
//clear the inputed file data
oLst.value = '';
}


Section Widget
Recent Articles