HTML5 localStorage for Offline Web Applications

HTML5 Local StorageThis article explains the local (client-side, browser) storage feature in HTML 5.

Since the introduction of HTML5, the technology is making an equal impact in the desktop and mobile space. It has completely changed what is possible with a Web interface. The new features are so powerful they bring Web pages closer to Apps. The new features include a plugin-free paradigm, with the introduction of tags like <video>, semantic markup like <header>, new form elements like email, and client-side storage. There is a constant drive to make browser-specific incompatibilities disappear.

The need for client-side storage has been felt with the increasing use of Web for e-commerce, with the aim to enhance the user experience by storing user preferences, shopping cart items and user-session information. Cookies have served Web developers for long, but have size limitations (4 KB each) and need to be transferred from client to server. The importance of offline storage has increased with Web app development using HTML5. There are times when the user is not connected to the Internet (e.g., composing an email in an airplane using Internet Web app to send later), or when data needs to be saved across sessions (e.g., a Web gaming app needs to store the session state, like the board positions for chess, so that the game can be resumed later).

With HTML5, two new objects are introduced for client-side storage. They are localStorage and sessionStorage. We will focus on localStorage in this article. New object sessionStorage is to store data for one session. Before introduction of localStorage in HTML5, developers had to choose from various vendor- or browser-specific options like Google Gears, userData in IE, and Local Shared Objects used in Adobe Flash Player.

An introduction to localStorage

localStorage is also called Web Storage or DOM storage. It is a database that can store only key-value pairs, and the value data-type should always be a string. At first, this might seem a serious limitation, but using along with JavaScript arrays and JSON we can achieve quite a bit. The good news is that this feature is supported in the latest versions of all browsers, both on desktop and mobile environments (Android and iOS). Also, the developer gets a huge 5 MB of storage per domain.

The code snippet below checks if the browser supports localStorage. The code in this article has been tested on Google Chrome 17.0.963.56 and Firefox 11.0. There is a small difference in behaviour when the code below is executed in IE: if the HTML file is opened locally (file:///C:/Web/mylocaltodo.html), IE throws an error — but if the same file is hosted on a server (even localhost), it just works!

In the following code, localStorage is an object under window and can be referenced via the window object, or directly as localStorage.

function browserCheck() {
  if (typeof(localStorage) == 'undefined' ) {
    alert('Your browser does not support HTML5 localStorage. Try upgrading.');
  }
}

localStorage support on browsers/platforms

This table lists the minimum version of each browser required to support localStorage:

IE Firefox Safari Chrome Opera iPhone Android
8.0+ 3.5+ 4.0+ 4.0+ 10.5+ 2.0+ 2.0+

Operations on localStorage

The methods of localStorage include setItem(), getItem(), removeItem() and clear(). Here is sample code to add a record to localStorage using setItem():

// max limit reached exception
try {
  localStorage.setItem(itemId, "Sample Value");
} catch (e) {
  if (e == QUOTA_EXCEEDED_ERR) {
    alert('Quota exceeded!');
  }
}

We will use getItem() and removeItem() in the Web app that we will develop. Note that localStorage.clear() removes all entries from the database, so use it with caution!

Debugging localStorage

The quick and easy way to view the localStorage database is in Chrome, as part of Developer Tools. Select the wrench menu at the top-right corner, go to Tools –> Developer Tools. Click the Resources tab, select Local Storage and then Local Files. The right pane should display key-value pairs (Figure 1), if the database has data. For debugging in Firefox, you need the Firebug extension.

Database view

Figure 1: Database view

MyLocalTodo: An example of the use of localStorage

Let’s try implementing a My Local Todo Web application, which can work offline, with minimal code. Besides localStorage, we will also use HTML5 features such as placeholder and required.

Application UI

Task listing

Figure 2: Task listing

In this simple to-do application, the first (text) field “New Task” is the task description; the second is task priority, and third the due date. Only the first is mandatory. If the user clicks “Create Task” without entering it, the browser shows a tooltip below the text field (Figure 3).

Tooltip notification for mandatory entry

Figure 3: Tooltip notification for mandatory entry

The default task priority is “Normal” and the due date is today.

<body onLoad="browserCheck();getTasks();">
    <h3>My Local Todo</h3>
        <form name="todoForm" onSubmit='addTask();'>
        <input type="text" size="50" name="task" placeholder="New Task" value="" autofocus required>
            <select id="priority">
                <option value="high">High</option>
                <option value="normal" selected>Normal</option>
                <option value="low">Low</option>
            </select>
        <input type="text" size="10" name="duedate" placeholder="dd/mm/yyyy">
        <input type="submit" value="Create Task">
        </form>
    <p id="theTaskList">Nothing much to do!</p>
</body>
  • Line 1: We check if the browser supports localStorage. If not, display an alert. We also display tasks already present in localStorage.
  • Line 3-12: Form with fields to accept task text, priority and due date. Form to be submitted via the “Create Task” button, which will add the task to localStorage.
  • Line 4: Compared to older HTML text fields, you find three new attributes: placeholder, autofocus and required. The first attribute displays a hint to the user, in grey text, about the expected purpose/format of the input. The autofocus attribute places the cursor in the text field before and after form submission. The required attribute makes input mandatory before the form can be submitted.
  • Line 5-9: Task priorities drop-down; “High”, “Normal” (default) and “Low” entries.
  • Line 10: Text field for due date; format suggested via placeholder.
  • Line 11: Form submission button “Create Task”.
  • Line 13: Space to list existing tasks. If no tasks are found, “Nothing much to do!” is displayed.

Operations and attributes of localStorage

There are three functions that perform operations on localStorage:

  • addTask(): Called when the user enters task details and clicks “Create Task”.
  • getTasks(): Retrieves all records from localStorage; called when the HTML page is loaded, and when a task is added, to update the list.
  • deleteTask(): Called when the user marks the task complete (ticks the check-box). The task is removed from localStorage.
function addTask()
{
    var values = new Array();
    var newDate = new Date();
    var itemId = newDate.getTime();
    var duedate = "";
    if (!document.forms["todoForm"]["duedate"].value)
    {
        duedate = fillDuedate();
    }
    else
    {
        duedate = document.forms["todoForm"]["duedate"].value;
    }
    values.push(document.forms["todoForm"]["task"].value);
    values.push(document.forms["todoForm"]["priority"].value);
    values.push(duedate);
    try {
        localStorage.setItem(itemId, values.join(';'));
    } catch (e) {
        if (e == QUOTA_EXCEEDED_ERR) {
            alert('Quota exceeded!');
        }
    }
    getTasks();
}
  • Line 3: Each record is stored as a key-value pair; fields are separated by a semicolon (;). A values() array is used to store each task record.
  • Line 4-5: Key is generated using the current time in milliseconds, on the assumption that there will always be at least a few milliseconds delay between the creation of two tasks — so, this is always unique.
  • Line 7-14: Check if the due date is entered; if not, insert today’s date in dd/mm/yyyy format using the function fillDuedate(). If the user entered a due date, use it as-is, without validation, since our focus is on trying localStorage
  • Line 18-24: Insert the new record in the localStorage database. If the 5 MB quota allotted by the browser is exceeded, an exception is thrown. Better storage management ideas are needed!
  • Line 25: Fetch tasks already in localStorage and display, including the newly created one.
function getTasks()
{
    var i = 0;
    var currentTaskInHTML = "";
    var allTasksInHTML = "";
    if (localStorage.length == 0)
    {
        document.getElementById('theTaskList').innerHTML = "Nothing much to do!";
    }
    else
    {
        for (i=0; i<localStorage.length; i++)
        {
            var itemKey = localStorage.key(i);
            var values = localStorage.getItem(itemKey);
            values = values.split(";");
            currentTaskInHTML = '<input type=\"checkbox\"
                           name="\"task\" value="\"' + itemKey + '\"
                           onClick=\"deleteTask(' + itemKey + ')\"/> ' +
                           values[0]+ ' (' +values[1]+ ') [' +values[2]+
                           ']</BR>';
            allTasksInHTML += currentTaskInHTML;
        }
        document.getElementById('theTaskList').innerHTML = allTasksInHTML;
    }
}
  • Line 6: If there are no tasks in localStorage, it displays “Nothing much to do!”.
  • Line 12-23: Iterates through each record in the database, prepares HTML for check-box and task details.
  • Line 12: localStorage.length returns the number of records present in the database.
  • Line 14: Fetches the unique key using the function localStorage.key().
  • Line 15: Fetches the complete record — fields separated by semi-colon, using the unique key.
  • Line 16: Extracts fields, separating them using split().
  • Line 17-21: Creates an HTML form element for each task (check-box followed by task details). Inserts the function deleteTask() as part of the actions when the user completes a task and ticks the check-box. To delete a task from the database, we need the unique key — which is the value of each check-box.
  • Line 22: Appends tasks, and creates one HTML string that will be output below the “New Task” form, replacing the previous task list with the new one using the innerHTML attribute.
function deleteTask(key)
{
    localStorage.removeItem(key);
    getTasks();
}
  • Line 3: When the user clicks the check-box to mark it as complete, the record key is passed to deleteTask(), which uses localStorage.removeItem() to remove the record.
  • Line 4: The list of pending tasks is refreshed after the deletion.

Extending MyLocalTodo to a full application

Here are some ideas that could be implemented to build on the skeleton app:

  • Add colour to tasks: Differentiate tasks with different priorities with colours. Tasks past the due date can be in RED.
  • Make the due date “human-friendly”: Add flexibility to accept human-friendly due dates like “today”, “tomorrow”, “by next week”, “end of April”, etc.
  • Export/import tasks: Due to localStorage limitations, the task database is restricted to one browser. Extend the app to export records as a CSV file to make tasks portable across browsers, and for backup purposes.
  • Import CSV: The ability to import a CSV file into localStorage could allow the import of tasks from Outlook, Google Tasks, etc.
  • Integration with Phonebook: A more friendly to-do app could integrate with the Phonebook application. Tasks that involve communication (a phone call or email) can fetch the phone number/email address and add it to the task, to enable calling or emailing the person. For example: for a task like “call @Rohit on Sunday”, it displays “call Rohit (9911991199) on Sunday”, so that you can dial by clicking the number.
  • Add tags to group tasks: Each task can have a tag such as “personal”, “work”, “call” or “email”, to group the related tasks to be performed together.

Develop other applications using localStorage

Some quick ideas:

  • Phonebook: The MyLocalTodo example app can be modified to develop a Phonebook application.
  • Project Time Tracker: You can check out the monkeeTime application, which is a Project Time Tracker.

References

  • joaoN1x

    localStorage is the most compatible, even with limits on size usage on some browsers

    i created a really simple and efficient way of dealing with
    localStorage as database with all the whistles add/remove/update easy
    and quick

    can check in github https://github.com/joaoN1x/DBosta.js

  • Sumeet

    Great Job!

    I am working on a proposal and the RFP needs are to have a web application with offline capabilities. The Offline capabilities are required for an extensive data entry process where there are about 5 Chapters, tons of Grids and hundreds of Questions. This data entry process needs to be implemented through a web interface ensuring that the users from the countries who have unreliable internet connectivity can work offline for most part of the data entry work (may be for days and weeks) and once they are ready with the data entered, then the application should be able to sync it back to the main database….

    Do you think HTML5 is mature enough to handle such complex and high volume offline data??

    thanks and regards

    Sumeet

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
Open Source For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.