Todo App with IndexedDB

Todo App with IndexedDB

ยท

8 min read

Few weeks back, I was in the process of developing a chrome new tab extension where everything is accessible and offline. That's when I had an idea to build an offline todo app using IndexedDB. It stores your data in the browser and it's not removed from when the browser is closed. Almost all browsers supports IndexedDB without any installations or library. For any written code, you can read about the process and causes below the code.

First and foremost, let's build a form for the todo list:

<!-- todo will be appended here using javascript -->
<ul id="todo"></ul>

<input type="text" id="todo_input" placeholder="Enter Todo">

We append the stored todo after the javascript fetches the values from IndexedDB, so it's empty for now.

localhost_todo_ (2).png

Let's create a javascript file add it at the end of the .html file.

<script src="app.js"></script>

Todo with IndexedDB

Since some of the browsers doesn't support indexedDB, let's check if the variable is available in the browser.

if ( 'indexedDB' in window ) {
    /* indexedDB available */
}

Let's create a function initDB that connects use to indexedDB as soon as the document opens. We need database version and db name. We are using version as 1. We're using 3 callback functions when the indexedDB is opened. They are:

  • onupgradeneeded during this callback we would use createObjectStore to tell the database the basic structure.
  • onsuccess this callback is used when the DB connection is successfully established.
  • onerror we're just logging the error for now, but it will help you with any errors.
(function(){
    var db;
    var dbname  =   'todos';
    initDB(function() {
        console.log('db connected!');
    });

    function initDB(callback){
        var version =   1;
        var request =   indexedDB.open(dbname, version);

        request.onupgradeneeded = function(e) {
            db = e.target.result;
            e.target.transaction.onerror = databaseError;
            // define the unique key
            db.createObjectStore('todo', { keyPath: 'timeStamp' });
        };

        request.onsuccess   =   function(e) {
            db  =   e.target.result;
            callback();
        };

        request.onerror =   databaseError;
    }

    // use general function for all errors
    function databaseError(e) {
        console.error('IndexedDB error:', e);
    }
})();

If you see db connected! in your console, you're now connected. db is the global variable in the function to perform CRUD operation. Let's create a function to add a Todo.

function addTodo( text, callback ){
    var transaction =   db.transaction(['todo'], 'readwrite');
    var store       =   transaction.objectStore('todo');
    var request     =   store.put({
       text: text,
       timeStamp: Date.now(), // used as Index for the table
       isdone: false
    });

    transaction.oncomplete  =   function(e) {
      callback();
    };

    request.onerror =   databaseError;
}

Now the function to get all the todo:

function getTodo() {
    var transaction = db.transaction(['todo'], 'readonly');
    var store = transaction.objectStore('todo');

    var keyRange = IDBKeyRange.lowerBound(0);
    var cursorRequest = store.openCursor(keyRange);

    var data = [];
    cursorRequest.onsuccess = function(e) {
        var result = e.target.result;
        // If there's data, add it to array
        if (result) {
            data.push(result.value);
            result.continue();

        // Reach the end of the data
        } else {
            var html    = '';
            data.forEach(function(eachdata){
                html += `<li>
                    <input type="checkbox" id="checkbox_${eachdata.timeStamp}" value="${eachdata.timeStamp}">
                    <label for="checkbox_${eachdata.timeStamp}">${eachdata.text}</label>
                </li>`;
            });

            if ( html == '' ) {
                // if there is no todo, show "all done" message
                html = '<p>Done with all todo!</p>';
            }

            var appendField = document.querySelector('#todo');
            // append all the todo to  `#todo`
            appendField.innerHTML = html;


            var checkboxes  =   document.querySelectorAll("#todo input");
            for (const _checkbox of checkboxes) {
                _checkbox.addEventListener('change', function(e) {
                    if ( e.target.checked === true ) {
                        // delete todo function
                        deleteTodo(e.target.value);
                    }
                });
            }
        }
    };
}

Important step: Now we can call getTodo inside initDB call. So your update code looks like:

initDB(function() {
    console.log('db connected!');
    getTodo();
});

Let's add function to delete todos:

function deleteTodo(index) {
    var transaction = db.transaction(["todo"], "readwrite");
    var objectStore = transaction.objectStore("todo");

    var objectStoreRequest = objectStore.delete(parseInt(index));

    objectStoreRequest.onsuccess = function(event) {
        // render new values from the database
        getTodo();
    };
}

Finally, we can execute the keypress event listener to connect all the function together to add data.

var todofield   =   document.getElementById('todo_input');
todofield.addEventListener('keydown', function(e){
    if (!e) e = window.event;
    var keyCode = e.keyCode || e.which;
    if (keyCode == '13'){
        addTodo(
            todofield.value,
            function(){
                todofield.value = '';
                getTodo();
            }
        );
        return false;
    }
});

Now type your todo and press enter in your todo input.

localhost_todo_ (3).png

Here is a working code, if you need to download: gist.github.com/hemnathmouli/29d652f4d4c816..

if you need have any questions