David Ruttka

I make computers do things

Getting Started With HTML5 Local Storage

Recently I posted about getting started with HTML5’s offline app support. Now let’s look at local storage. This will allow you to store app data locally until it can be posted, which could be very powerful in conjunction with offline support. Even while the user is online, it can be used to periodically auto-save the work in progress to be restored in case of a crash.

Fundamentals

Let’s take a look at the Storage interface as defined by w3.org.

1
2
3
4
5
6
7
8
interface Storage {
    readonly attribute unsigned long length;
    DOMString? key(in unsigned long index);
    getter DOMString getItem(in DOMString key);
    setter creator void setItem(in DOMString key, in DOMString value);
    deleter void removeItem(in DOMString key);
    void clear();
};

It’s basically just another key-value store, but the slick thing is that the browser will hold onto it even if you reboot. Below is a shot of what Chrome’s Developer Tools might show after using the demo app I’m about to walkthrough with you.

image

Lifetime and Scope

According to the current version of the spec, localStorage is specific to an origin. You can see this in the screenshot above where our demo data is housed under fiddle.jshell.net. It “should” only expire due to a request from the user or for security reasons. Furthermore, security exceptions are thrown if the script attempting to access the storage isn’t from the same origin.

Example Fiddle

When I first started coding, the quintessential example was “Hello World!” Then, it seemed that Twitter clones were becoming the next big thing in beginner tutorials. Today, it seems that Todo Apps are quickly forging ahead.

Note: Several of my decisions in this demo app were made for the sake of focusing on the localStorage aspect, throwing it up quickly on jsfiddle, and keeping other concepts out of the way. For example, I didn’t use backbone or Modernizr because many of you might be unfamiliar with them and wouldn’t know what you were looking at. Similarly, I didn’t spend a lot of time thinking through architectural decisions, clean code, etc. Take this for what it’s worth: an example.

Alright, let’s show where we’re going. For the sake of this example, we can add tasks. We can clear them, but we can’t remove them. If we refresh the page, navigate away and come back, restart the browser, or even reboot, the latest list of tasks should still be there. Here’s what the UI looks like:

image

Checking Browser Support

The first thing I put in the jQuery ready function was a check as to whether the browser supports localStorage. If not, I fade out the functional divs and show a banner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// This will change the view if localStorage isn't available. It
// returns true or false
function verifyLocalStorage()
{
    if (!window.localStorage)
    {
        $('.section').fadeOut();
        $('#not-supported').show();
        return false;
    }

    return true;
}

// Set it all up
$(function(){

    // Notify the user if local storage isn't supported
    if (verifyLocalStorage() === false)
        return;

    // ...
});

The UI looks like this if the check fails:

image

Restoring The Data

Next, I set up an empty tasks array and a function to add a single task to the view, to the array, and update localStorage. Then in the jQuery ready function, I fetch local storage and reuse the same addTask method to restore the previous data. Note that I use JSON.stringify and JSON.parse to handle the array. If you don’t do this, the object serializes to localStorage as [object Object] and you’re toast. Also, note that I fallback to the JSON string for an empty object if there’s nothing in localStorage; otherwise an exception would be thrown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// In memory tasks store
tasks = [];

// This adds a task
function addTask(task)
{
    // To the view
    $('<li>')
        .appendTo('#task-list')
        .text(task.name);

    // To memory
    tasks[tasks.length] = task;

    // In storage
    localStorage['tasks'] = JSON.stringify(tasks);
}

// Set it all up
$(function(){

    //...

    // Get the last stored tasks and restore them to the UI.
    // Default to an empty array
    var oldTasks = JSON.parse(localStorage.getItem('tasks') || '[]');
    $.each(oldTasks, function(i, e) {
        addTask(e);
    });

    //...
});

Adding To The List

Adding to the list is fairly simple. I just capture the form submission which handles the user’s click or keypress, push the input into a new object, and call the same addTask method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(function(){

    // ...

    // Set up a handler for submission
    $(':submit').click(function(){

        // Add the new task
        var newTask = { 'name' : $('#task-name').val() };
        addTask(newTask);

        // Clear the input
        $('#task-name').val('');

        // Don't post
        return false;
    });

    // ...

});

Clearing The Data

For the sake of this example, let’s look at the method that clears all of the localStorage for the active origin. Note that in our case, since we only have one key (‘tasks’), this is functionally equivalent to localStorage.removeItem(‘tasks’). If you wanted to remove a single item, you could write logic similar to addTask to remove it from the view and the array, then update local storage.

Note: My use of localStorage.clear() instead of calling .removeItem(‘tasks’) or pushing an updated array into the ‘tasks’ item is another one of those decisions I made just for the sake of the example.

Here’s the code to capture the link click and blow away all of the localStorage for our site.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// This will get rid of all the tasks
function clearAll()
{
    // From memory
    tasks = [];

    // From the view
    $('#task-list').empty();

    // In storage
    localStorage.clear();
}

// Set it all up
$(function(){

    // ...

    // Setup a handler for Clear All
    $('#clear').click(function(){
        clearAll();
        return false;
    });

    // ...
});

Live Demo

You can see all of this live and completed on jsfiddle.

This post originally appeared on The DevStop.