Warning: localStorage may throw SecurityError in private
browsing mode or when storage quota is exceeded. Always wrap storage operations in try-catch
blocks.
2. IndexedDB Compatibility and Polyfills
Feature
API
Description
Support
Database
indexedDB.open(name, version)
Open/create database
IE10+
Object Store
db.createObjectStore(name, options)
Create table equivalent
In onupgradeneeded callback
Transaction
db.transaction(stores, mode)
Create transaction scope
Modes: readonly, readwrite
CRUD Operations
store.add/get/put/delete()
Data manipulation
Returns IDBRequest
Cursors
store.openCursor()
Iterate over records
Event-driven iteration
Vendor Prefixes
webkitIndexedDB, mozIndexedDB
Legacy browser support
Prefix normalization needed
Example: IndexedDB vendor prefix normalization
// Normalize IndexedDB across browserswindow.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;// Check if IndexedDB is availableif (!window.indexedDB) { console.warn('IndexedDB not supported');}// Promise wrapper for IndexedDBvar IDB = { open: function(name, version) { return new Promise(function(resolve, reject) { var request = indexedDB.open(name, version); request.onerror = function() { reject(request.error); }; request.onsuccess = function() { resolve(request.result); }; request.onupgradeneeded = function(event) { var db = event.target.result; // Handle upgrades if (!db.objectStoreNames.contains('store')) { db.createObjectStore('store', { keyPath: 'id', autoIncrement: true }); } }; }); }, add: function(db, storeName, data) { return new Promise(function(resolve, reject) { var transaction = db.transaction([storeName], 'readwrite'); var store = transaction.objectStore(storeName); var request = store.add(data); request.onsuccess = function() { resolve(request.result); }; request.onerror = function() { reject(request.error); }; }); }, get: function(db, storeName, key) { return new Promise(function(resolve, reject) { var transaction = db.transaction([storeName], 'readonly'); var store = transaction.objectStore(storeName); var request = store.get(key); request.onsuccess = function() { resolve(request.result); }; request.onerror = function() { reject(request.error); }; }); }};// UsageIDB.open('myDatabase', 1) .then(function(db) { return IDB.add(db, 'store', { name: 'John', age: 30 }); }) .then(function(id) { console.log('Added with ID:', id); }) .catch(function(error) { console.error('IndexedDB error:', error); });
Note: IndexedDB is asynchronous and event-driven. Use Promise
wrappers or libraries like idb or localForage for easier API.
3. Cookie API and Document.cookie Extensions
Operation
Method
Options
Notes
Set Cookie
document.cookie = 'key=value'
expires, path, domain, secure, samesite
Append, not replace
Get Cookie
Parse document.cookie
-
Returns all cookies as string
Delete Cookie
Set with past expiration
expires=Thu, 01 Jan 1970
Must match path/domain
Size Limit
~4KB per cookie
~20-50 cookies per domain
Browser dependent
HttpOnly
Server-side only flag
Cannot access via JavaScript
Security feature
SameSite
SameSite=Strict|Lax|None
CSRF protection
Modern browsers
Example: Cookie utility library
// Cookie utility functionsvar Cookies = { set: function(name, value, options) { options = options || {}; var cookieString = encodeURIComponent(name) + '=' + encodeURIComponent(value); // Expires if (options.expires) { var expires; if (typeof options.expires === 'number') { expires = new Date(); expires.setTime(expires.getTime() + options.expires * 24 * 60 * 60 * 1000); } else { expires = options.expires; } cookieString += '; expires=' + expires.toUTCString(); } // Path cookieString += '; path=' + (options.path || '/'); // Domain if (options.domain) { cookieString += '; domain=' + options.domain; } // Secure if (options.secure) { cookieString += '; secure'; } // SameSite if (options.sameSite) { cookieString += '; samesite=' + options.sameSite; } document.cookie = cookieString; }, get: function(name) { var nameEQ = encodeURIComponent(name) + '='; var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = cookies[i].trim(); if (cookie.indexOf(nameEQ) === 0) { return decodeURIComponent(cookie.substring(nameEQ.length)); } } return null; }, remove: function(name, options) { options = options || {}; options.expires = -1; this.set(name, '', options); }, getAll: function() { var cookies = {}; var cookieArray = document.cookie.split(';'); for (var i = 0; i < cookieArray.length; i++) { var cookie = cookieArray[i].trim(); var parts = cookie.split('='); if (parts.length === 2) { cookies[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } } return cookies; }, has: function(name) { return this.get(name) !== null; }};// UsageCookies.set('username', 'john_doe', { expires: 7, // 7 days path: '/', secure: true, sameSite: 'Lax'});var username = Cookies.get('username');console.log(username); // 'john_doe'Cookies.remove('username');var allCookies = Cookies.getAll();console.log(allCookies);
Example: Storage wrapper using cookies as fallback
// Universal storage with cookie fallbackvar UniversalStorage = { _useLocalStorage: (function() { try { var test = '__test__'; localStorage.setItem(test, test); localStorage.removeItem(test); return true; } catch(e) { return false; } })(), setItem: function(key, value, days) { if (this._useLocalStorage) { try { localStorage.setItem(key, value); return; } catch(e) {} } // Fallback to cookies Cookies.set(key, value, { expires: days || 365 }); }, getItem: function(key) { if (this._useLocalStorage) { try { return localStorage.getItem(key); } catch(e) {} } // Fallback to cookies return Cookies.get(key); }, removeItem: function(key) { if (this._useLocalStorage) { try { localStorage.removeItem(key); return; } catch(e) {} } // Fallback to cookies Cookies.remove(key); }};// Usage - automatically uses localStorage or cookiesUniversalStorage.setItem('token', 'abc123');var token = UniversalStorage.getItem('token');
Warning: Cookies have ~4KB size limit per cookie and are sent
with every HTTP request. Use localStorage for larger data. Never store sensitive data in cookies without
Secure and HttpOnly flags.
4. WebSQL to IndexedDB Migration Polyfills
Feature
WebSQL
IndexedDB
Status
Standard
W3C Working Draft
W3C Recommendation
WEBSQL DEPRECATED
API Style
SQL queries
NoSQL key-value
Different paradigms
Browser Support
Chrome, Safari (removed in new versions)
All modern browsers
Migrate to IndexedDB
Storage Limit
~5-10MB default
~50MB+ (quota API)
IndexedDB more flexible
Transactions
SQL transactions
Object store transactions
Both support ACID
Example: WebSQL migration helper
// WebSQL migration utilityvar WebSQLMigration = { hasWebSQL: function() { return 'openDatabase' in window; }, hasIndexedDB: function() { return 'indexedDB' in window; }, migrateFromWebSQL: function(webSQLDB, indexedDBName, tableName, storeName) { return new Promise(function(resolve, reject) { if (!this.hasWebSQL() || !this.hasIndexedDB()) { reject(new Error('Required APIs not available')); return; } // Read from WebSQL webSQLDB.transaction(function(tx) { tx.executeSql('SELECT * FROM ' + tableName, [], function(tx, results) { var rows = []; for (var i = 0; i < results.rows.length; i++) { rows.push(results.rows.item(i)); } // Write to IndexedDB var request = indexedDB.open(indexedDBName, 1); request.onupgradeneeded = function(e) { var db = e.target.result; if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName, { keyPath: 'id' }); } }; request.onsuccess = function(e) { var db = e.target.result; var transaction = db.transaction([storeName], 'readwrite'); var store = transaction.objectStore(storeName); rows.forEach(function(row) { store.add(row); }); transaction.oncomplete = function() { resolve(rows.length); }; transaction.onerror = function() { reject(transaction.error); }; }; request.onerror = function() { reject(request.error); }; }); }); }.bind(this)); }};// Usagevar db = openDatabase('mydb', '1.0', 'My Database', 2 * 1024 * 1024);WebSQLMigration.migrateFromWebSQL(db, 'myIndexedDB', 'users', 'users') .then(function(count) { console.log('Migrated ' + count + ' records'); }) .catch(function(error) { console.error('Migration failed:', error); });
Note: WebSQL is deprecated and removed from modern browsers.
Migrate existing WebSQL data to IndexedDB or use libraries like localForage that abstract storage.
5. Memory Storage Fallback Implementations
Strategy
Persistence
Use Case
Limitations
In-Memory Object
Session only (cleared on reload)
Last resort fallback
No persistence, lost on refresh
sessionStorage
Tab/window session
Temporary data for session
Lost when tab closed
localStorage
Persistent across sessions
Primary storage method
May fail in private mode
Cookie
Persistent (with expiration)
Fallback for localStorage
4KB limit, sent with requests
IndexedDB
Persistent, large storage
Complex data, large datasets
Async API complexity
Example: Complete storage fallback implementation
// Storage facade with fallback chainvar StorageFacade = (function() { 'use strict'; var storageType = null; var memoryStorage = {}; // Test storage availability function testStorage(type) { try { var storage = window[type]; var test = '__storage_test__'; storage.setItem(test, test); storage.removeItem(test); return true; } catch(e) { return false; } } // Determine best available storage function init() { if (testStorage('localStorage')) { storageType = 'localStorage'; } else if (testStorage('sessionStorage')) { storageType = 'sessionStorage'; } else if ('cookie' in document) { storageType = 'cookie'; } else { storageType = 'memory'; } console.log('Using storage type:', storageType); } function setItem(key, value) { switch(storageType) { case 'localStorage': case 'sessionStorage': try { window[storageType].setItem(key, value); } catch(e) { console.warn('Storage quota exceeded, falling back to memory'); memoryStorage[key] = value; } break; case 'cookie': Cookies.set(key, value, { expires: 365 }); break; case 'memory': default: memoryStorage[key] = value; break; } } function getItem(key) { switch(storageType) { case 'localStorage': case 'sessionStorage': try { return window[storageType].getItem(key); } catch(e) { return memoryStorage[key] || null; } case 'cookie': return Cookies.get(key); case 'memory': default: return memoryStorage[key] || null; } } function removeItem(key) { switch(storageType) { case 'localStorage': case 'sessionStorage': try { window[storageType].removeItem(key); } catch(e) { delete memoryStorage[key]; } break; case 'cookie': Cookies.remove(key); break; case 'memory': default: delete memoryStorage[key]; break; } } function clear() { switch(storageType) { case 'localStorage': case 'sessionStorage': try { window[storageType].clear(); } catch(e) { memoryStorage = {}; } break; case 'cookie': var cookies = Cookies.getAll(); Object.keys(cookies).forEach(function(key) { Cookies.remove(key); }); break; case 'memory': default: memoryStorage = {}; break; } } function getStorageType() { return storageType; } // Initialize on load init(); // Public API return { setItem: setItem, getItem: getItem, removeItem: removeItem, clear: clear, getStorageType: getStorageType };})();// Usage - automatically uses best available storageStorageFacade.setItem('user', JSON.stringify({ name: 'John', age: 30 }));var user = JSON.parse(StorageFacade.getItem('user'));console.log('Using storage:', StorageFacade.getStorageType());