LocalStorage and IndexedDB
Written September 2012- specs and browser support will change!
Local Storage
- IE 8+. All browsers give 5Mb of key-value storage.
Even if it is supported it might not be enabled. Features may throw exceptions, so use Modernizr - DiveIntoHtml5
- MDN
- w3Schools
localStorage["key"] = 29; //or localStorage.setItem(key, value); //always coerce types var find = parseInt(localStorage["key"], 10); //or localStorage.getItem(key) var size = localStorage.length;
- storage is scoped to the origin (http://website/). NB file:// is not allowed.
- sessionStorage is for as long as the browser is open.
- localStorage is persistent across browser restarts.
- It's dead simple but strings-only, synchronous and IO-limited (so can be slow)
IndexedDb
- IE 10+, FF and Chrome. As at end 2012, Chrome is prefixed (IE+FF not), no support in Safari (which still has WebSQL) or any mobile browsers.
It doesn't work in PrivateMode - MDN
- Almost everything returns asynchronous requests. Always hook on .onerror and .onsuccess.
Open/create the database
- Initialize and upgrade database through .onupgradeneeded (.createObjectStore, .createIndex, .deleteObjectStore)
- ObjectStores can have autoincrement key generators or you explicitly set them.
function createIDatabase(useDb) { //get the prefixed version. No prefixes: IE 10, latest FF window.indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; //try to open or create it (optional: add version integer) var request = indexedDB.open("filmDatabase", 1); request.onerror = function () { $("#log").text("Database is not available"); }; // Create object stores request.onupgradeneeded = function (event) { $("#log").text("Creating database"); var db = event.target.result; // Create an objectStore with keyPath var objectStore = db.createObjectStore("films", { keyPath: "filmId" }); // Create an non-unique index objectStore.createIndex("name", "name", { unique: false }); }; request.onsuccess = function () { var db = request.result; db.onerror = function (event) { //global error handler eg database wrong version $("#log").text("Database error " + event.target.error.name); }; useDb(db); }; }
ObjectStore- saving
- ObjectStore = table. You get at it via database transaction.
- Transactions name the tables and whether readonly (default) or readwrite.
- You can .add (=insert) or .put (=upsert)
function saveToIndexDb() { createIDatabase(function (db) { var storeName = "films"; //open an array of stores (or db.objectStoreNames for all) var transaction = db. transaction([storeName], //Optional, default is readonly. Or "readwrite" IDBTransaction.READ_WRITE); //or var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || { READ_WRITE: "readwrite" }; //nb an error on transaction causes a rollback var objectStore = transaction.objectStore(storeName); var film = { filmId: parseInt($("input[name='id']").val(), 10), name: $("input[name='title']").val() }; //add = insert, put = upsert var addRequest = objectStore.add(film); addRequest.onerror = function () { $("#log").text("Error adding " + film.filmId); }; }); }
ObjectStore - reading
.get(key)
function readIndexDb() { var id = parseInt($("input[name='id']").val(), 10); createIDatabase(function (db) { var storeName = "films"; db.transaction(storeName) .objectStore(storeName) .get(id) .onsuccess = function (event) { var film = event.target.result; if (!film) { $("#log").text("No film with this id"); } else { $("#log").text("Found film " + film.name); } }; }); }
.openCursor(range) often via .index(name)
function readAllIndexDb() { createIDatabase(function (db) { var storeName = "films"; // lowerbound-upperbound (or .only(value)) var range = IDBKeyRange.bound("G", "I"); db.transaction(storeName) .objectStore(storeName) .index("name")//can use an index .openCursor(range) .onsuccess = function (event) { //this is called for each item in cursor, plus at the end (cursor is undefined) var cursor = event.target.result; if (!cursor) { //finished } else { //if using index, cursor.key is the index value //if you openKeyCursor() cursor.value is not the object, it's the key var film = cursor.value; $("#log").append("
" + film.name); cursor.continue(); } }; }); }
How much storage?
navigator.storage.estimate().then(function(estimate) { })
Usage
Quota
function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } if(navigator.storage) { navigator.storage.estimate().then(function(estimate) { let meter= document.getElementById("storage"); meter.value = estimate.usage; meter.max = estimate.quota; document.getElementById("usage").value = formatBytes(estimate.usage, 2); document.getElementById("quota").value = formatBytes(estimate.quota,2); }); }