Search loading

IndexedDB API

IndexedDB is a low-level API for client-side storage of significant amounts of structured data, including files/blobs. This API uses indexes to enable high-performance searches of this data. While Web Storage is useful for storing smaller amounts of data, it is less useful for storing larger amounts of structured data. IndexedDB provides a solution.

Key concepts and usage

IndexedDB is a transactional database system, like an SQL-based RDBMS. However, unlike SQL-based RDBMSes, which use fixed-column tables, IndexedDB is a JavaScript-based object-oriented database. IndexedDB lets you store and retrieve objects that are indexed with a key; any objects supported by the structured clone algorithm can be stored. You need to specify the database schema, open a connection to your database, and then retrieve and update data within a series of transactions.

Synchronous and asynchronous

Operations performed using IndexedDB are done asynchronously, so as not to block applications. IndexedDB originally included both synchronous and asynchronous APIs. The synchronous API was intended for use only with Web Workers but was removed from the spec because it was unclear whether it was needed. However, the synchronous API may be reintroduced if there is enough demand from web developers.

Storage limits and eviction criteria

There are a number of web technologies that store data of one kind or another on the client side (i.e. on your local disk). IndexedDB is the most commonly talked about one. The process by which the browser works out how much space to allocate to web data storage and what to delete when that limit is reached is not simple, and differs between browsers.

Using IndexedDB

IndexedDB is a way for you to persistently store data inside a user's browser. Because it lets you create web applications with rich query abilities regardless of network availability, your applications can work both online and offline.

Basic pattern

The basic pattern that IndexedDB encourages is the following:
  1. Open a database.
  2. Create an object store in the database.
  3. Start a transaction and make a request to do some database operation, like adding or retrieving data.
  4. Wait for the operation to complete by listening to the right kind of DOM event.
  5. Do something with the results (which can be found on the request object).

Creating and structuring the store

Using an experimental version of IndexedDB

Cheque if your browser have support for indexedDB:

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

Opening a database

Cheque if your browser have support for indexedDB:

// Let us open our database
var request = window.indexedDB.open("MyTestDatabase", 3); 

The open request doesn't open the database or start the transaction right away. The call to the open() function returns an IDBOpenDBRequest object with a result (success) or error value that you handle as an event. Most other asynchronous functions in IndexedDB do the same thing - return an IDBRequest object with the result or error. The result for the open function is an instance of an IDBDatabase.

The second parameter to the open method is the version of the database. The version of the database determines the database schema — the object stores in the database and their structure. If the database doesn't already exist, it is created by the open operation, then an onupgradeneeded event is triggered and you create the database schema in the handler for this event. If the database does exist but you are specifying an upgraded version number, an onupgradeneeded event is triggered straight away, allowing you to provide an updated schema in its handler

Generating handlers

The first thing you'll want to do with almost all of the requests you generate is to add success and error handlers:

request.onerror = function(event) {
  // Do something with request.errorCode!
};
request.onsuccess = function(event) {
  // Do something with request.result!
}; 

Which of the two functions, onsuccess() or onerror(), gets called? If everything succeeds, a success event (that is, a DOM event whose type property is set to "success") is fired with request as its target. Once it is fired, the onsuccess() function on request is triggered with the success event as its argument. Otherwise, if there was any problem, an error event (that is, a DOM event whose type property is set to "error") is fired at request. This triggers the onerror() function with the error event as its argument.

Now, assuming that the user allowed your request to create a database, and you've received a success event to trigger the success callback; What's next? The request here was generated with a call to indexedDB.open(), so request.result is an instance of IDBDatabase, and you definitely want to save that for later. Your code might look something like this:

The first thing you'll want to do with almost all of the requests you generate is to add success and error handlers:

var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function(event) {
  alert("Why didn't you allow my web app to use IndexedDB?!");
};
request.onsuccess = function(event) {
  db = event.target.result;
};

Creating or updating the version of the database

When you create a new database or increase the version number of an existing database (by specifying a higher version number than you did previously, when Opening a database), the onupgradeneeded event will be triggered and an IDBVersionChangeEvent object will be passed to any onversionchange event handler set up on request.result (i.e., db in the example). In the handler for the upgradeneeded event, you should create the object stores needed for this version of the database:


// This event is only implemented in recent browsers
request.onupgradeneeded = function(event) {
  // Save the IDBDatabase interface
  var db = event.target.result;

  // Create an objectStore for this database
  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
}; 

In this case, the database will already have the object stores from the previous version of the database, so you do not have to create these object stores again. You only need to create any new object stores, or delete object stores from the previous version that are no longer needed. If you need to change an existing object store (e.g., to change the keyPath), then you must delete the old object store and create it again with the new options.

Structuring the database

Now to structure the database. IndexedDB uses object stores rather than tables, and a single database can contain any number of object stores. Whenever a value is stored in an object store, it is associated with a key. There are several different ways that a key can be supplied depending on whether the object store uses a key path or a key generator.

The following table shows the different ways the keys are supplied:
Key Path (keyPath) Key Generator (autoIncrement) Description
No No

This object store can hold any kind of value, even primitive values like numbers and strings. You must supply a separate key argument whenever you want to add a new value.

Yes No

This object store can only hold JavaScript objects. The objects must have a property with the same name as the key path.

No Yes

This object store can hold any kind of value. The key is generated for you automatically, or you can supply a separate key argument if you want to use a specific key.

Yes Yes

This object store can only hold JavaScript objects. Usually a key is generated and the value of the generated key is stored in the object in a property with the same name as the key path. However, if such a property already exists, the value of that property is used as key rather than generating a new key.

he is on exemple of costumer data defined:

// This is what our customer data looks like.
const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
];
Now let's look at creating an IndexedDB to store our data:

const dbName = "the_name";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // Handle errors.
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // Create an objectStore to hold information about our customers. We're
  // going to use "ssn" as our key path because it's guaranteed to be
  // unique - or at least that's what I was told during the kickoff meeting.
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // Create an index to search customers by name. We may have duplicates
  // so we can't use a unique index.
  objectStore.createIndex("name", "name", { unique: false });

  // Create an index to search customers by email. We want to ensure that
  // no two customers have the same email, so use a unique index.
  objectStore.createIndex("email", "email", { unique: true });

  // Use transaction oncomplete to make sure the objectStore creation is
  // finished before adding data into it.
  objectStore.transaction.oncomplete = function(event) {
    // Store values in the newly created objectStore.
    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
    customerData.forEach(function(customer) {
      customerObjectStore.add(customer);
    });
  };
};

Using a key generator

Setting up an autoIncrement flag when creating the object store would enable the key generator for that object store. By default this flag is not set.

With the key generator, the key would be generated automatically as you add the value to the object store. The current number of a key generator is always set to 1 when the object store for that key generator is first created. Basically the newly auto-generated key is increased by 1 based on the previous key.

We can create another object store with the key generator as below:

// Open the indexedDB.
var request = indexedDB.open(dbName, 3);

request.onupgradeneeded = function (event) {

    var db = event.target.result;

    // Create another object store called "names" with the autoIncrement flag set as true.
    var objStore = db.createObjectStore("names", { autoIncrement : true });

    // Because the "names" object store has the key generator, the key for the name value is generated automatically.
    // The added records would be like:
    // key : 1 => value : "Bill"
    // key : 2 => value : "Donna"
    customerData.forEach(function(customer) {
        objStore.add(customer.name);
    });
}; 

Adding, retrieving, and removing data

Before you can do anything with your new database, you need to start a transaction. Transactions come from the database object, and you have to specify which object stores you want the transaction to span. Once you are inside the transaction, you can access the object stores that hold your data and make your requests. Next, you need to decide if you're going to make changes to the database or if you just need to read from it. Transactions have three available modes: readonly, readwrite, and versionchange.

To change the "schema" or structure of the database—which involves creating or deleting object stores or indexes—the transaction must be in versionchange mode. This transaction is opened by calling the IDBFactory.open method with a version specified. (In WebKit browsers, which have not implemented the latest specification, the IDBFactory.open method takes only one parameter, the name of the database; then you must call IDBVersionChangeRequest.setVersion to establish the versionchange transaction.)

o read the records of an existing object store, the transaction can either be in readonly or readwrite mode. To make changes to an existing object store, the transaction must be in readwrite mode. You open such transactions with IDBDatabase.transaction. The method accepts two parameters: the storeNames (the scope, defined as an array of object stores that you want to access) and the mode (readonly or readwrite) for the transaction. The method returns a transaction object containing the IDBIndex.objectStore method, which you can use to access your object store. By default, where no mode is specified, transactions open in readonly mode.

Adding data to the database

If you've just created a database, then you probably want to write to it. Here's what that looks like:

var transaction = db.transaction(["customers"], "readwrite");
// Note: Older experimental implementations use the deprecated constant IDBTransaction.READ_WRITE instead of "readwrite".
// In case you want to support such an implementation, you can write:
// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE); 

Now that you have a transaction, you'll need to get the object store from it. Transactions only let you have an object store that you specified when creating the transaction. Then you can add all the data you need.


// Do something when all the data is added to the database.
transaction.oncomplete = function(event) {
  alert("All done!");
};

transaction.onerror = function(event) {
  // Don't forget to handle errors!
};

var objectStore = transaction.objectStore("customers");
customerData.forEach(function(customer) {
  var request = objectStore.add(customer);
  request.onsuccess = function(event) {
    // event.target.result === customer.ssn;
  };
}); 

The result of a request generated from a call to add() is the key of the value that was added. So in this case, it should equal the ssn property of the object that was added, since the object store uses the ssn property for the key path. Note that the add() function requires that no object already be in the database with the same key. If you're trying to modify an existing entry, or you don't care if one exists already, you can use the put() function, as shown below in the Updating an entry in the database section.

Removing data from the database

Removing data is very similar:

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // It's gone!
}; 

Getting data from the database

Now that the database has some info in it, you can retrieve it in several ways. First, the simple get(). You need to provide the key to retrieve the value, like so:

var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // Handle errors!
};
request.onsuccess = function(event) {
  // Do something with the request.result!
  alert("Name for SSN 444-44-4444 is " + request.result.name);
}; 

Updating an entry in the database

Now we've retrieved some data, updating it and inserting it back into the IndexedDB is pretty simple. Let's update the previous example somewhat:


var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // Handle errors!
};
request.onsuccess = function(event) {
  // Get the old value that we want to update
  var data = event.target.result;

  // update the value(s) in the object that you want to change
  data.age = 42;

  // Put this updated object back into the database.
  var requestUpdate = objectStore.put(data);
   requestUpdate.onerror = function(event) {
     // Do something with the error
   };
   requestUpdate.onsuccess = function(event) {
     // Success - the data is updated!
   };
}; 

So here we're creating an objectStore and requesting a customer record out of it, identified by its ssn value (444-44-4444). We then put the result of that request in a variable (data), update the age property of this object, then create a second request (requestUpdate) to put the customer record back into the objectStore, overwriting the previous value.

Using a cursor

Using get() requires that you know which key you want to retrieve. If you want to step through all the values in your object store, then you can use a cursor. Here's what it looks like:


var objectStore = db.transaction("customers").objectStore("customers");

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

The openCursor() function takes several arguments. First, you can limit the range of items that are retrieved by using a key range object that we'll get to in a minute. Second, you can specify the direction that you want to iterate. In the above example, we're iterating over all objects in ascending order. The success callback for cursors is a little special. The cursor object itself is the result of the request (above we're using the shorthand, so it's event.target.result). Then the actual key and value can be found on the key and value properties of the cursor object. If you want to keep going, then you have to call continue() on the cursor. When you've reached the end of the data (or if there were no entries that matched your openCursor() request) you still get a success callback, but the result property is undefined.

Using an index

Storing customer data using the SSN as a key is logical since the SSN uniquely identifies an individual. (Whether this is a good idea for privacy is a different question, and outside the scope of this article.) If you need to look up a customer by name, however, you'll need to iterate over every SSN in the database until you find the right one. Searching in this fashion would be very slow, so instead you can use an index.


// First, make sure you created index in request.onupgradeneeded:
// objectStore.createIndex("name", "name");
// Otherwize you will get DOMException.

var index = objectStore.index("name");

index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
}; 

The "name" cursor isn't unique, so there could be more than one entry with the name set to "Donna". In that case you always get the one with the lowest key value.

Full indexedDB Example

In this codelab, you learn how to integrate a indexedDB into an existing application to save your data on web store.

What you'll learn
  1. How to create object stores and indexes
  2. How to create, retrieve, update, and delete values (or CRUD)
  3. How to use cursors
What you'll need
  1. Suggest Chrome 55 or above (cheque browser compatibility).
  2. A basic understanding of Git, and Chrome DevTools.
  3. The sample code.
  4. A text editor.
  5. A local web server.

Get the sample code

Clone the GitHub repository from the command line over SSH:
$ git clone git@github.com:engailtonoliveira/sticky_nodes.git
Clone the GitHub repository from the command line over SSH:
$ git clone https://github.com/engailtonoliveira/sticky_nodes.git

Html Content


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name='description' content='Stick Note. Probably the best note web app there is.'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Sticky Notes</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet">
    <link href="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote.css" rel="stylesheet">
    <link rel="shortcut icon" href="#">
    <link href="css/style.css" rel="stylesheet">
</head>
<body>
    <section id="bodylistStickyNotes">
        <section id="stickyNotes">
            <div id="listStickyNotes">
            </div>
            <div class="floatBtn">
                <div class='addpubbutton' id="createStickyNotes">
                    <button type='button' class='btn btn-circle floatingButton'>
                        <div class='styleSVGFloatingButton'>
                            <svg version="1.1"  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" style="" xml:space="preserve">
                                <path d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216v216c0,11.046,8.954,20,20,20s20-8.954,20-20V276h216c11.046,0,20-8.954,20-20C512,244.954,503.046,236,492,236z"/>
                            </svg>
                        </div>
                    </button>
                </div>
            </div>
        </section>
        <section id="createUpdatelistStickyNotes" style="display: none">
            <div id="summernote"></div>
            <div class="floatBtn">
                <div class='addSecPubButton' id="saveStickyNotes">
                    <button type='button' class='btn btn-circle floatingButton'>
                        <div class='styleSVGFloatingButton'>
                            <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 215 215" style="" xml:space="preserve">
                                <path style="fill:#030104;" d="M186,21.718V0H28v16H0v199h215V50.414L186,21.718z M169,56h-9V17h9V56z M45,17h76v39H45V17z M175,192H39v-91h136V192z"/>
                                <rect style="fill:#030104;" x="58" y="122" width="74" height="18"/>
                                <rect style="fill:#030104;" x="58" y="153" width="97" height="18"/>
                            </svg>
                        </div>
                    </button>
                </div>
                <div class='addpubbutton' id="backHome">
                    <button type='button' class='btn btn-circle floatingButton'>
                        <div class='styleSVGFloatingButton'>
                            <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 27.02 27.02" style="" xml:space="preserve"><path style="fill:#030104;" d="M3.674,24.876c0,0-0.024,0.604,0.566,0.604c0.734,0,6.811-0.008,6.811-0.008l0.01-5.581 c0,0-0.096-0.92,0.797-0.92h2.826c1.056,0,0.991,0.92,0.991,0.92l-0.012,5.563c0,0,5.762,0,6.667,0c0.749,0,0.715-0.752,0.715-0.752V14.413l-9.396-8.358l-9.975,8.358C3.674,14.413,3.674,24.876,3.674,24.876z"/><path style="fill:#030104;" d="M0,13.635c0,0,0.847,1.561,2.694,0l11.038-9.338l10.349,9.28c2.138,1.542,2.939,0,2.939,0L13.732,1.54L0,13.635z"/><polygon style="fill:#030104;" points="23.83,4.275 21.168,4.275 21.179,7.503 23.83,9.752"/></svg>
                        </div>
                    </button>
                </div>
            </div>
        </section>
    </section>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
    <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.11/summernote.js"></script>
    <script src="js/indexedDB.js"></script>
    <script>
        $(document).ready(function() {
            //displayPubList();
        });

        var w = window.innerWidth;
        var h = window.innerHeight;
        setDimenssionContent(h, w);

        $(window).resize(function() {
            let w = window.innerWidth;
            let h = window.innerHeight;
            setDimenssionContent (h, w);
        });

        function setDimenssionContent (height, width) {
            $('#bodylistStickyNotes').height(height);
            $('#stickyNotes').width($('#bodylistStickyNotes').width());
            $('#stickyNotes').height($('#bodylistStickyNotes').height());
        }

        function init_summernote(element) {
            $(element).summernote({
                height: 80,
                toolbar: false,
                disableResizeEditor: true,
            });
        }

        function init_toast(content) {
            $.toast({
                text: content,
                heading: false,
                showHideTransition: 'fade',
                allowToastClose: false,
                hideAfter: 3000,
                stack: 5,
                position: 'bottom-left',
                textAlign: 'left',
                loader: false,
                //loaderBg: '#9EC600',  // Background color of the toast loader
                beforeShow: function () {}, // will be triggered before the toast is shown
                afterShown: function () {
                    $("#savelistStickyNotes ").hide();
                }, // will be triggered after the toat has been shown
                beforeHide: function () {}, // will be triggered before the toast gets hidden
                afterHidden: function () {
                }
            });
        }
    </script>
</body>
</html>
                                        

CSS Content


html {
    box-sizing: unset;
}

body {
    margin: 0;
    padding: 0;
    font:14px/1.5 "Helvetica Neue", Helvetica, Arial, san-serif;
    overflow-x: hidden;
    background-image: url("../images/background.jpg");
}

.card {
    border-radius: 0;
}

.note-editor .note-editable {
    line-height: 1;
    font-size: 32px;
}

.note-editable {
    background-color: yellow !important;
}

.table-bordered table-striped td, .table-bordered table-striped th {
    border: 1px solid #000 !important;
}

#createUpdateSticke {
    display: none;
}

.addpubbutton {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 60px;
    height: 60px;
    margin-right: 18px;
    margin-bottom: 18px;
    z-index: 10000;
}

.addSecPubButton {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 40px;
    height: 40px;
    margin-right: 26px;
    margin-bottom: 86px;
    z-index: 10000;
}

.btn-circle {
    opacity: 0.6;
    width: 100%;
    height: 100%;
    line-height: 42px;
    padding: 0;
    border-radius: 100px!important;
    box-shadow: 2px 2px 5px 3px rgba(31, 24, 24, 0.7);
    background-color: #3783c5;
}

.styleSVGFloatingButton {
    fill: #fff;
    position: relative;
    width: 50%;
    margin: 15px auto 0;
}

#saveStickyNotes > button > div.styleSVGFloatingButton {
    margin-top: 4px;
}

#saveSticke .styleSVGFloatingButton {
    margin: auto;
}

#saveStickyNotes .btn-circle {
    box-shadow: 1px 1px 5px 1px rgba(31, 24, 24, 0.7);
}

.contentSummernote {
    margin: 14px 14px 0 10px;
    overflow: visible;
    -webkit-box-shadow: 2px 2px 5px 3px rgba(31, 24, 24, 0.6);  /* Safari 3-4, iOS 4.0.2 - 4.2, Android 2.3+ */
    -moz-box-shadow:    2px 2px 5px 3px rgba(31, 24, 24, 0.6);  /* Firefox 3.5 - 3.6 */
    box-shadow:         2px 2px 5px 3px rgba(31, 24, 24, 0.6);  /* Opera 10.5, IE 9, Firefox 4+, Chrome 6+, iOS 5 */
    position: relative;
}

.remove-sticke {
        opacity: 0.6;
    position: absolute;
    border-radius: 50%;
    background: #4c5b5c;
    color: #fff;
    top: -10px;
    right: -10px;
    z-index: 10;
}

button:focus {
    -moz-outline-radius: unset !important;
    outline: unset !important;
}

#bodyStickyNotes {
    position: relative;
}

#stickyNotes{
    position: absolute;
    top: 0;
    left: 0;
    overflow-y: hide;
    overflow-x: auto;
}                                    

JavaScript Content


'use strict';
(function () {
  const DB_NAME = 'indexeddb-stickynote';
  const DB_VERSION = 1; // Use a long long for this value (don't use a float)
  const DB_STORE_NAME = 'sticky_note';

  var db;

  // Used to keep track of which view is displayed to avoid uselessly reloading it
  var current_view_pub_key;

  function openDb() {
    console.log("openDb ...");
    var req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onsuccess = function (evt) {
      // Better use "this" than "req" to get the result to avoid problems with
      // garbage collection.
      // db = req.result;
      db = this.result;
      console.log("openDb DONE");
      displayPubList();
    };
    req.onerror = function (evt) {
      console.error("openDb:", evt.target.errorCode);
    };
    req.onupgradeneeded = function (evt) {
      console.log("openDb.onupgradeneeded");
      var store = evt.currentTarget.result.createObjectStore(
        DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });

      store.createIndex('content', 'content', { unique: false });
    };
  }

  const summernoteDefault = {
    elementId: "#summernote",
    content: "[content data to be edited]",
    operation: "new"
  }

  const btnAddNewStickyNotes = document.getElementById("createStickyNotes");
  const btnSaveStickyNotes = document.getElementById("saveStickyNotes");
  const btnBackHome = document.getElementById("backHome");

  const contentLinstStickyNotes = document.getElementById("stickyNotes");
  const contentCreateUpdateStickyNotes = document.getElementById("createUpdatelistStickyNotes");

  /**
   * @param {string} store_name
   * @param {string} mode either "readonly" or "readwrite"
   */
  function getObjectStore(store_name, mode) {
    var tx = db.transaction(store_name, mode);
    return tx.objectStore(store_name);
  }

  function displayPubList(store) {
    console.log("displayPubList");

    if (typeof store == 'undefined')
      store = getObjectStore(DB_STORE_NAME, 'readonly');

    var listStickyNotes = $('#listStickyNotes');
    listStickyNotes.empty();

    var req;
    req = store.count();
    // Requests are executed in the order in which they were made against the
    // transaction, and their results are returned in the same order.
    // Thus the count text below will be displayed before the actual pub list
    // (not that it is algorithmically important in this case).
    req.onsuccess = function(evt) {
      console.log("There are " + evt.target.result + "record(s) in the object store.");
    };
    req.onerror = function(evt) {
      console.error("add error", this.error);
      displayActionFailure(this.error);
    };

    var i = 0;
    req = store.openCursor();
    req.onsuccess = function(evt) {
      var cursor = evt.target.result;

      // If the cursor is pointing at something, ask for the data
      if (cursor) {
        console.log("displayPubList cursor:", cursor);
        req = store.get(cursor.key);
        req.onsuccess = function (evt) {
          var value = evt.target.result;
          var list_item = $('<div class="contentSummernote" data-id="'+cursor.key+'">\
                          <button class="remove-sticke">X</button>\
                          <div  id="summernote'+cursor.key+'"></div>\
                          </div>');

          listStickyNotes.append(list_item);

          init_summernote('#summernote'+cursor.key);
          $('#summernote'+cursor.key).summernote('code', value.content);
          $('.note-statusbar').hide();
          $('#summernote'+cursor.key).summernote('disable');
        };

        // Move on to the next object in store
        cursor.continue();

        // This counter serves only to create distinct ids
        i++;
      } else {
        console.log("No more entries");
      }
    };
  }

  function addObjectStore(content) {
    console.log("addObjectStore ...");

    // Create a new object ready to insert into the IDB
    var content = $(summernoteDefault.elementId).summernote('code');
    var obj = { content: content };
    //console.log(newItem);

    var store = getObjectStore(DB_STORE_NAME, 'readwrite');
    var objectStoreRequest;

    try {
      objectStoreRequest = store.add(obj);
    } catch (evt) {
      if (evt.name == 'DataCloneError')
        console.log("This engine doesn't know how to clone a Blob, use Firefox");
      throw evt;
    }

    objectStoreRequest.onsuccess = function(event) {
      console.log("Success ...");
      displayActionSuccess();
    };

    objectStoreRequest.onerror = function(event) {
      console.error("addObjectStore", this.error);
      displayActionFailure(this.error);
    };

  }

  function updateObjectStore(index) {
    console.log("updateObjectStore ...");

    // Create a new object ready to insert into the IDB
    var content = $(summernoteDefault.elementId).summernote('code');

    var store = getObjectStore(DB_STORE_NAME, 'readwrite');

    var req = store.openCursor();
    req.onerror = function(evt) {
      console.error("case if have an error", this.error);
    };

    req.onsuccess = function(evt) {
      var cursor = evt.target.result;
      if(cursor){
        if(cursor.value.id == index){//we find by id an user we want to update
          var updateData = cursor.value;

          updateData.content = content;


          var req = cursor.update(updateData);
          req.onsuccess = function(e){
            console.log("Update success!!");
             displayActionSuccess("Your data was save");
          }
          req.onerror = function(e){
            console.log("Update failed: ",this.error);
            displayActionFailure(this.error);
          }
          return;
        }
        cursor.continue();
        console.log(cursor.value.id);
      } else {
        console.error('Entries displayed.');
      }
    }

  }

  /**
   * @param {number} key
   * @param {IDBObjectStore=} store
   */
  function deletePublication(key, store) {
    console.log("deletePublication:", arguments);

    if (typeof store == 'undefined')
      store = getObjectStore(DB_STORE_NAME, 'readwrite');

    var req = store.openCursor();
    req.onerror = function(evt) {
      console.error("case if have an error", this.error);
      displayActionFailure(this.error);
    };

    req.onsuccess = function(event) {
      var cursor = event.target.result;
      if(cursor) {
        if(cursor.value.id == key) {
          var request = cursor.delete();
          request.onsuccess = function() {
            displayActionSuccess("Deletion successful");
            displayPubList(store);
            return;
          };
        }
        cursor.continue();
      } else {
        console.log('Entries displayed.');
      }
    };
  }

  function displayActionSuccess(msg) {
    msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
    init_toast(msg);
  }

  function displayActionFailure(msg) {
    msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
    init_toast(msg);
  }

  function registerSummernote(summernote, callbackOperation) {
    $("#summernote").summernote('destroy');
    $(summernote.elementId).summernote({
      focus: true,
      disableResizeEditor: false,
      toolbar: false,
      callbacks: {
        onInit: function(e) {
          $(this).summernote("fullscreen.toggle");
          $(this).summernote('code', summernote.content);
          callbackOperation(summernote.operation);
        },onChange: function(contents, $editable) {
            btnSaveStickyNotes.style.display = "block";
        }
      }
    });
  }

  function addEventListeners() {
    console.log("addEventListeners");

      btnAddNewStickyNotes.addEventListener("click", (evt, params) => {
        console.log("Params: ",params);
        console.log("new sticky notes");
        setStickyNotes(summernoteDefault);

      });

      btnSaveStickyNotes.addEventListener("click", (evt) => {
        console.log("save sticky notes");
        var data_operation = evt.currentTarget.getAttribute("data-operation");

        if (data_operation == "new") {
          addObjectStore();
        }else {
          var stickyNoteId = evt.currentTarget.getAttribute("data-id");
          updateObjectStore(stickyNoteId);
        }
        evt.currentTarget.style.display = "none";
      });

      btnBackHome.addEventListener("click", () => {
        console.log("back home");
        contentCreateUpdateStickyNotes.style.display = "none";
        var pub_list = $("#listStickyNotes");
        pub_list.empty();
        displayPubList();
        contentLinstStickyNotes.style.display = "block";
      });

  }

  $("#listStickyNotes").on("click","button.remove-sticke",function(evt) {
    console.log("Remove ...");

      var stickyNoteId = $(this).parent().attr("data-id");
      swal({
          title: "Are you sure?",
          text: "You will not be able to recover this sticky note!",
          type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-danger",
          confirmButtonText: "Yes, cancel it!",
          cancelButtonText: "No, keep plx!",
          closeOnConfirm: true,
          closeOnCancel: true,
          showLoaderOnConfirm: true
      },function(isConfirm) {
          if (isConfirm) {
            deletePublication(stickyNoteId);
          }
      });
      evt.stopPropagation();
  });

  $("#listStickyNotes").on("click",".contentSummernote", function () {
    console.log("Update ...");
    var stickyNoteId = $(this).attr("data-id");

    var cloneObjectStickyNote = summernoteDefault;
    cloneObjectStickyNote.content = $("#summernote"+stickyNoteId).summernote('code');
    cloneObjectStickyNote.operation = "update";
    btnSaveStickyNotes.setAttribute("data-id", stickyNoteId);
    setStickyNotes(cloneObjectStickyNote);

  });

  function setStickyNotes(summernote) {
    console.log("Object Summernote", summernote);
    contentLinstStickyNotes.style.display = "none";
    contentCreateUpdateStickyNotes.style.display = "block";


    registerSummernote(summernote, function(operation) {
      console.log("callback...");
      btnSaveStickyNotes.setAttribute("data-operation", operation);
    });

  }
  openDb();
  addEventListeners();

})(); // Immediately-Invoked Function Expression (IIFE)                                     

Specifications

Specification Status Comment
Indexed Database API 2.0 Recommendation Initial definition
Indexed Database API 3.0 Recommendationt

Browser compatiblity

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

Desktop

Feature Chrome Edge Firefox(Gecko) Internet Explorer Opera Safari
Basic Support

23 webkit

24 (unprefixed)

38 (prefixes deprecated)

57 (prefixes removed)

Yes

10.0 moz

16.0

10 15

7.1, pratial

10

Available in workers

(Yes) (unprefixed)

38 (prefixes deprecated)

38 (prefixes deprecated)

57 (prefixes removed)

yes

37.0

Yes Yes 10
Available in privacy mode yes No support No support No support No support No support
IDBLocaleAwareKeyRange No support No support 43.0 No support No support No support
Indexed Database 2.0 58 ? ? ? 45 10.1

Mobile

Feature Android Webview Chrome for Andrid Edge Firefox Mobile (Gecko) Firefox OS IE/Edge Phone Opera Mobile Safari Mobile
Basic Support

(Yes) (unprefixed)

38 (prefixes deprecated)

57 (prefixes removed)

(Yes) (unprefixed)

38 (prefixes deprecated)

57 (prefixes removed)

Yes 22.0 1.0.1 10 22

8, partial

10

Available in workers

(Yes) (unprefixed)

38 (prefixes deprecated)

57 (prefixes removed)

(Yes) (unprefixed)

38 (prefixes deprecated)

57 (prefixes removed)

Yes 37.0 Yes Yes Yes 10
Available in privacy mode No support No support No support No support No support No support ? ?
IDBLocaleAwareKeyRange No support No support No support 43.0 2.5 No support No support No support
Indexed Database 2.0 58 58 ? ? ? 10.1 45 ?
loading