static void

LocalStorage and IndexedDB

Written September 2012- specs and browser support will change! Updated 2024.

Local Storage

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;

IndexedDb

Open/create the database

async function createOrRecreateDatabase() {
    // Open a connection to the database
    const request = indexedDB.open("CountryDb", 1);

    // If the database does not exist - or needs updating
    request.onupgradeneeded = event => {
        const db = event.target.result;

        // If the Countries object store already exists, delete it
        if (db.objectStoreNames.contains("Countries")) {
            db.deleteObjectStore("Countries");
        }

        // Create the Countries object store with a key path of 'id' and autoIncrement set to true
        const countryStore = db.createObjectStore("Countries", { keyPath: "id", autoIncrement: true });

        // Create an index on the 'Name' property
        countryStore.createIndex("by_name", "LowerCaseName", { unique: false });

        // Add initial data
        // for case insensitive search, add a lowercase property to the object
        const countries = [
            { Name: "Australia", ShortName: "AU", LowerCaseName: "Australia".toLowerCase() },
            { Name: "Belgium", ShortName: "BE", LowerCaseName: "Belgium".toLowerCase() },
            { Name: "Brazil", ShortName: "BR", LowerCaseName: "Brazil".toLowerCase() },
            { Name: "Canada", ShortName: "CA", LowerCaseName: "Canada".toLowerCase() },
            { Name: "China", ShortName: "CN", LowerCaseName: "China".toLowerCase() },
           // Add more countries as needed...
        ];

        countries.forEach(country => {
            countryStore.add(country);
        });
    };

    request.onerror = event => {
        console.error("Database error: ", event.target.error);
    };

    request.onsuccess = event => {
        console.log("Database created successfully");
    };
}

ObjectStore- saving

async function updateCountryName(id, newName) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("CountryDb", 1);

        request.onerror = event => {
            console.error("Database error: ", event.target.error);
            reject("Failed to open DB");
        };

        request.onsuccess = event => {
            const db = event.target.result;
            const transaction = db.transaction("Countries", "readwrite");
            const store = transaction.objectStore("Countries");

            const getRequest = store.get(id);
            getRequest.onerror = event => {
                console.error("Error fetching country: ", event.target.error);
                reject("Failed to fetch country");
            };

            getRequest.onsuccess = event => {
                const data = event.target.result;
                data.Name = newName; // Update the name
                data.LowerCaseName = newName.toLowerCase();

                const putRequest = store.put(data);
                putRequest.onerror = event => {
                    console.error("Error updating country: ", event.target.error);
                    reject("Failed to update country");
                };

                putRequest.onsuccess = () => {
                    resolve("Country updated successfully");
                };
            };
        };
    });
}

ObjectStore - reading

.get(key)

async function fetchAllCountries() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("CountryDb", 1);

        request.onerror = event => {
            console.error("Database error: ", event.target.error);
            reject("Failed to open DB");
        };

        request.onsuccess = event => {
            const db = event.target.result;
            const transaction = db.transaction("Countries", "readonly");
            const store = transaction.objectStore("Countries");
            const getAllRequest = store.getAll();

            getAllRequest.onerror = event => {
                console.error("Error fetching countries: ", event.target.error);
                reject("Failed to fetch countries");
            };

            getAllRequest.onsuccess = event => {
                resolve(event.target.result);
            };
        };
    });
}

.openCursor(range) often via .index(name)

async function searchCountriesByNamePrefix(namePrefix) {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("CountryDb", 1);

        request.onerror = event => {
            console.error("Database error: ", event.target.error);
            reject("Failed to open DB");
        };

        request.onsuccess = event => {
            const db = event.target.result;
            const transaction = db.transaction("Countries", "readonly");
            const store = transaction.objectStore("Countries");
            const index = store.index("by_name");
            // Convert the prefix to lowercase for case-insensitive search
            const lowercasedPrefix = namePrefix.toLowerCase();
            const range = IDBKeyRange.bound(lowercasedPrefix, lowercasedPrefix + '\uffff');
            const cursorRequest = index.openCursor(range);
            const results = [];
            let count = 0; // Counter for the number of records

            cursorRequest.onerror = event => {
                console.error("Error searching countries: ", event.target.error);
                reject("Failed to search countries");
            };

            cursorRequest.onsuccess = event => {
                const cursor = event.target.result;
                if (cursor && count < 100) {
                    if (cursor.value.Name.toLowerCase().startsWith(lowercasedPrefix)) {
                        results.push(cursor.value);
                    }
                    cursor.continue();
                } else {
                    // No more results
                    resolve(results);
                }
            };
        };
    });
}

Demo

Using dbOperations.js (all local)

Add a New Country

Search Countries

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];
}