HTML5 Storage: IndexedDB (Simple Easy to Understand)

Please note: This only works as intended in browsers that support the HTML5 IndexedDB API. Browser compatibility: Firefox 16 onwards, IE 10 and Chrome 12 onwards

What is IndexedDB?

The IndexedDB Database or better known it as IndexedDB, a new HTML5 web database that allows our HTML5 web application to store data inside a user’s browser. Unlike HTML5 localstorage which lets us store data using a simple key-value pair only, the IndexedDB is more powerful and useful for applications that requires to store a large amount of data. In addition, with its rich queries abilities, these applications can load faster and more responsive than ever.

Why Using IndexedDB?

The W3C has announced that the Web SQL database (another option of HTML5 storage) is a deprecated specification, and web developers should not use the technology anymore. Instead, web developers should use the replacement – IndexedDB, a new HTML5 data storage to manipulate their data on client-side.

The Key Concept of IndexedDB

  • IndexedDB is NOT same as a Relational Database: Please keep in mind that IndexedDB is Object-Oritend, which differs from relational database. This important and fundamental difference affects the way you design and build your applications.
  • IndexedDB store key-value pairs: The value is an object that can have 1 or more properties. The key can be based on a key generator or derived by a key path that defines the path to the key value.
  • The IndexedDB API is mostly asynchronous: Tha API doesn’t return any values, instead we need a callback function to get the returning data.
  • IndexedDB does not use Structured Query Language (SQL): It uses queries on an index that produces a cursor, which you use to iterate across the result set.
  • IndexedDB prevents applications from accessing data with a different origin: You can only retrieve the data from the same origin (domain / port).

The Outline of This Article

Following decribes exactly what you will learn in this article:

  • Open a database and start the transaction.
  • Create an object store.
  • Record and read data.
  • Listen to the DOM event.
  • Display the data(result) from the indexedDB
*Here is an important message for you, the IndexedDB is still an experimental technology, and this technology is not yet solidified at the moment, so every browser vendors may have different implementations of the IndexedDB API.

Please check out the demo before we start the tutorial.

View Demo

Get Started

Since we know that the IndexedDB API is still evolve, so we need to include the following prefixes of implementation. Besides, for best practice, we should always provide fallback content for unsupported browsers.

//prefixes of implementation that we want to test
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;

//prefixes of window.IDB objects
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange

if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB.")
}

Opening an IndexedDB Database

Before we create the database, the very first thing to do is to prepare some data for the database. Let say we have some customer information as shown below:

const customerData = [
{ id: “00-01”, name: “Bill”, age: 35, email: “bill@company.com” },
{ id: “00-02”, name: “Donna”, age: 32, email: “donna@home.org” }
];

2 data entries, each entry consists of the information of ID, name, age and email of the customer.

Before we can insert the data into the database, we need to open our database using the open() method as shown below:

var db;

var request = window.indexedDB.open("newDatabase”, 1);
    request.onerror = function(event) {
    console.log("error: ");
};

request.onsuccess = function(event) {
    db = request.result;
    console.log("success: "+ db);
};

request.onupgradeneeded = function(event) {
}

As you can see we have opened a database with the name “newDatabase” and the version of the database. All IndexedDB databases are stored in the same origin with the web application / website. Example, if myblog.com might has a database named “newDatabase” and mybusiness.com also might has a totally distinct database named “newDatabase”.

The open() method has accepts 2 parameters, the first is the name ofthe database. It will checks whether the database named “newDatabase” is already exist or not, if it is exist, then it will open the database, else it will create a new one. The second paramter of open() method is the version of the database, which allows you to update the schema of the database.

Onsuccess handler

If everything succeeds, then a success event “onsuccess” is fired with request as its target and we have save the request’s result for later use by assigning it to db variable.

Onerror handler

If the process of opening database fail, then an error event “onerror” is fired.

Onupgradeneeded handler

If you want to update the database, or to create, delete or modify the database, then you have to implement the onupgradeneeded handler or which will be called as part of a versionchange transaction that allows you to make any changes on the database. Please bear in mind that, the “onupgradeneeded” handler is the only place for you to alter the structure of database.

Structuring the Database

Again, IndexedDB is NOT same as a Relational Database. IndexedDB uses object stores to store data rather than tables. Whenever a value is stored in an object store, it is associated with a key. The interesting of IndexedDB is it allows us to create indices on any object store. An index lets us to access the values stored in object store using the value of a property of the stored object. This may sound confusing now, but you may know it better after go through the entire lesson.

The following code illustrates how we create the object store and insert the pre-prepared data into it:

request.onupgradeneeded = function(event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore("customers", {keyPath: "id"});
    for (var i in customerData) {
        objectStore.add(customerData[i]);
    }
}

We create an object store using createObjectStore() method. This method accepts 2 parameters: – name of the store and a parameter object. In this case, we have named the object store as “customers” and defined a keyPath that is the property that makes an individual object in the store unique. In this example, we have use the “id” as keyPath, which is unique value in the object store, and you must make sure that the “id” property must be present in every objects in the object store.

Once the object store is created, we can start adding the data into it using for loop.

Manually Adding Data

You may probably want to manually add extra data into the database, then here is how you should write the function:

function add() {
    var request = db.transaction(["customers"], "readwrite").objectStore("customers").add({ id: "00-03", name: "Kenny", age: 19, email: "kenny@planet.org" });

    request.onsuccess = function(event) {
        alert("Kenny has been added to your database.");
    };

    request.onerror = function(event) {
        alert("Unable to add data\r\nKenny is aready exist in your database! ");
    }
}

We have just manually added a new data named with “Kenny” to the object store “customer” using add() method.

Before we can do anything (read, write, modify) to our database, we have to start use a transaction. The transaction() method is used to specify which object stores you want the transaction to span. The transaction() mthod accepts 3 parameters (second and third are optional): – First is the list of object store you want to deal with, second is whether you want to read only or read and write to the object, third is the versionchange.

The transaction method let you have the object store that you specified and insert, modify or delete the data that you need. In this case, we insert the data using add() method.

Retrieving Data

Let’s retrieve the data from the database. We can retrieve the data using get() method.

function read() {
    var transaction = db.transaction(["customers"]);
    var objectStore = transaction.objectStore("customers");
    var request = objectStore.get("00-03");

    request.onerror = function(event) {
        alert("Unable to retrieve daa from database!");
    };
    
    request.onsuccess = function(event) {
        // Do something with the request.result!
        if(request.result) {
            alert("Name: " + request.result.name + ", Age: " + request.result.age + ", Email: " + request.result.email);
        } else {
        alert("Kenny couldn’t be found in your database!");
        }
    };
}

We use get() method to retrieve the data we need from object store. Since we already set the id of object as keyPath earlier, so the get() method will look up the object that has the same id value. This will return us the object named “Kenny”which is the object that we manually added in the previous function.

Retrieving All Data

If you want get all data instead of one from object store, then you may need to use a cursor. Here is another function that using cursor to retrieve all data from object store:

function readAll() {
    var objectStore = db.transaction("customers").objectStore("customers");

    objectStore.openCursor().onsuccess = function(event) {
        var cursor = event.target.result;
        if (cursor) {
            alert("Name for id " + cursor.key + " is " + cursor.value.name + ", Age: " + cursor.value.age + ", Email: " + cursor.value.email);
            cursor.continue();
        } else {
            alert("No more entries!");
        }
    };
}

As you can see, we implement the openCursor() method to accomplish the goal. The openCursor() is used to iterate over multiple records in a database. It can accepts several parameters, such as limit the range items, the direction that we want to iterate and etc. In this case, we leave it no parameters.

The cursor object itself is the result of the request. We have implement the continue() function to continues with the next iteration in the loop. When the loop reached end, then we will get the alert with content “No more entries!”.

Removing Data

Removing data from object store is very similar to other functions that we have just learnt. Here is how the code looks like:

function remove() {
    var request = db.transaction(["customers"], "readwrite").objectStore("customers").delete("00-03");
    
    request.onsuccess = function(event) {
        alert("Kenny’s entry has been removed from your database.");
    };
}

If you want to remove data from object store, then you may need to use delete() method. You have to pass the keyPath of the object that you want to remove as paramter to the delete() method. In this case, we remove the object with named “Kenny” which we added via add function just now.

The HTML

We have just wrote all the functions, and now is the time to display the data using the onclick event that binded to HTML button.

We have prepared 4 HTML buttons. Each of the button is used to trigger the Javascript function that we wrote earlier in this article. Save your document and view it in supported browser. Have fun! 🙂

The Final Code

<!DOCTYPE html>

<head>
    <title>IndexedDb Demo | onlyWebPro.com</title>
    <meta name="viewport" content="width=device-width, user-scalable=no">
</head>

<body class="demo_page">
    <div class="demo_content">

        <div class="info">
            <script type="text/javascript">
                //prefixes of implementation that we want to test
                window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;

                //prefixes of window.IDB objects
                window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
                window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange

                if (!window.indexedDB) {
                    window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")
                }

                const customerData = [
                    { id: "00-01", name: "Bill", age: 35, email: "bill@company.com" },
                    { id: "00-02", name: "Donna", age: 32, email: "donna@home.org" }
                ];


                var db;
                var request = window.indexedDB.open("newDatabase", 1);

                request.onerror = function (event) {
                    console.log("error: ");
                };

                request.onsuccess = function (event) {
                    db = request.result;
                    console.log("success: " + db);
                };

                request.onupgradeneeded = function (event) {
                    var db = event.target.result;
                    var objectStore = db.createObjectStore("customers", { keyPath: "id" });
                    for (var i in customerData) {
                        objectStore.add(customerData[i]);
                    }
                }

                function read() {
                    var transaction = db.transaction(["customers"]);
                    var objectStore = transaction.objectStore("customers");
                    var request = objectStore.get("00-03");
                    request.onerror = function (event) {
                        alert("Unable to retrieve daa from database!");
                    };
                    request.onsuccess = function (event) {
                        // Do something with the request.result!
                        if (request.result) {
                            alert("Name: " + request.result.name + ", Age: " + request.result.age + ", Email: " + request.result.email);
                        } else {
                            alert("Kenny couldn't be found in your database!");
                        }
                    };
                }

                function readAll() {
                    var objectStore = db.transaction("customers").objectStore("customers");

                    objectStore.openCursor().onsuccess = function (event) {
                        var cursor = event.target.result;
                        if (cursor) {
                            alert("Name for id " + cursor.key + " is " + cursor.value.name + ", Age: " + cursor.value.age + ", Email: " + cursor.value.email);
                            cursor.continue();
                        }
                        else {
                            alert("No more entries!");
                        }
                    };
                }

                function add() {
                    var request = db.transaction(["customers"], "readwrite")
                        .objectStore("customers")
                        .add({ id: "00-03", name: "Kenny", age: 19, email: "kenny@planet.org" });

                    request.onsuccess = function (event) {
                        alert("Kenny has been added to your database.");
                    };

                    request.onerror = function (event) {
                        alert("Unable to add data\r\nKenny is aready exist in your database! ");
                    }

                }

                function remove() {

                    var request = db.transaction(["customers"], "readwrite")
                        .objectStore("customers")
                        .delete("00-03");
                    request.onsuccess = function (event) {
                        alert("Kenny's entry has been removed from your database.");
                    };
                }

            </script>
            <h1>HTML5 IndexedDB Demo:</h1>
            <button onclick="read()">Read single data from indexedDb</button>
            <button onclick="readAll()">Read all data from indexedDb</button>
            <button onclick="add()">Add data to indexedDb</button>
            <button onclick="remove()">Delete data from indexedDb</button>
            <p><a href="http://www.onlywebpro.com/2012/12/23/html5-storage-indexeddb/">Check Out The HTML5 Storage:
                    IndexedDB (Simple Easy to Understand)</a></p>
        </div>
        <!-- END info -->

    </div>
    <!-- END demo_content -->
</body>

</html>

View Demo

Conclusion

The HTML5 IndexedDB API is very useful and powerful. You can leverage it to create rich, online and offline HTML5 application. In addition, with IndexedDB API, you can cache data to make traditional web applications especially mobile web applications load faster and more responsive without need to retrieve data from the web server each time.

You may also interested in:

More HTML5 Web Development Tutorial?

Check out the complete list of onlyWebPro’s HTML5 Web Development tutorial here!


Posted

in

by

Advertisement