javascript

examples

examples.js
/**
 * ========================================
 * 11.1 localStorage and sessionStorage - Examples
 * ========================================
 *
 * These examples demonstrate web storage APIs.
 * Run in browser console for best results.
 */

/**
 * ========================================
 * EXAMPLE 1: Basic localStorage Operations
 * ========================================
 */
console.log('--- Example 1: Basic localStorage Operations ---');

// Store values
localStorage.setItem('username', 'john_doe');
localStorage.setItem('theme', 'dark');
localStorage.setItem('fontSize', '16');

// Retrieve values
console.log('Username:', localStorage.getItem('username'));
console.log('Theme:', localStorage.getItem('theme'));
console.log('Font size:', localStorage.getItem('fontSize'));

// Check for non-existent key
console.log('Missing key:', localStorage.getItem('nonexistent')); // null

// Count items
console.log('Total items:', localStorage.length);

// Get key by index
console.log('First key:', localStorage.key(0));

// Remove specific item
localStorage.removeItem('fontSize');
console.log('After removal:', localStorage.getItem('fontSize')); // null

// Clean up example data
localStorage.removeItem('username');
localStorage.removeItem('theme');

/**
 * ========================================
 * EXAMPLE 2: Basic sessionStorage Operations
 * ========================================
 */
console.log('\n--- Example 2: Basic sessionStorage Operations ---');

// Session storage works the same way
sessionStorage.setItem('sessionId', 'abc123');
sessionStorage.setItem('tempData', 'This will be gone when tab closes');

console.log('Session ID:', sessionStorage.getItem('sessionId'));
console.log('Temp data:', sessionStorage.getItem('tempData'));

// Clear session storage
sessionStorage.clear();
console.log('After clear:', sessionStorage.length);

/**
 * ========================================
 * EXAMPLE 3: Storing Objects with JSON
 * ========================================
 */
console.log('\n--- Example 3: Storing Objects with JSON ---');

const user = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  preferences: {
    theme: 'dark',
    notifications: true,
    language: 'en',
  },
};

// Store object
localStorage.setItem('user', JSON.stringify(user));
console.log('Stored user object');

// Retrieve and parse
const savedUser = JSON.parse(localStorage.getItem('user'));
console.log('Retrieved user:', savedUser);
console.log('User name:', savedUser.name);
console.log('User theme:', savedUser.preferences.theme);

// Clean up
localStorage.removeItem('user');

/**
 * ========================================
 * EXAMPLE 4: Storing Arrays
 * ========================================
 */
console.log('\n--- Example 4: Storing Arrays ---');

const todos = [
  { id: 1, text: 'Learn JavaScript', completed: true },
  { id: 2, text: 'Learn Web Storage', completed: false },
  { id: 3, text: 'Build a project', completed: false },
];

// Store array
localStorage.setItem('todos', JSON.stringify(todos));

// Retrieve array
const savedTodos = JSON.parse(localStorage.getItem('todos'));
console.log('Retrieved todos:', savedTodos);

// Modify and save back
savedTodos[1].completed = true;
localStorage.setItem('todos', JSON.stringify(savedTodos));

console.log('Updated todos:', JSON.parse(localStorage.getItem('todos')));

// Clean up
localStorage.removeItem('todos');

/**
 * ========================================
 * EXAMPLE 5: Type Conversion Issues
 * ========================================
 */
console.log('\n--- Example 5: Type Conversion Issues ---');

// Numbers become strings
localStorage.setItem('count', 42);
const count = localStorage.getItem('count');
console.log('Count value:', count);
console.log('Count type:', typeof count); // "string"
console.log('Count + 1 (wrong):', count + 1); // "421"
console.log('Count + 1 (correct):', Number(count) + 1); // 43

// Booleans become strings
localStorage.setItem('isActive', true);
const isActive = localStorage.getItem('isActive');
console.log('isActive value:', isActive);
console.log('isActive type:', typeof isActive); // "string"
console.log('isActive === true:', isActive === true); // false!
console.log('isActive === "true":', isActive === 'true'); // true

// Fix: Use JSON
localStorage.setItem('countJSON', JSON.stringify(42));
localStorage.setItem('isActiveJSON', JSON.stringify(true));

console.log('JSON count:', JSON.parse(localStorage.getItem('countJSON'))); // 42 (number)
console.log('JSON isActive:', JSON.parse(localStorage.getItem('isActiveJSON'))); // true (boolean)

// Clean up
['count', 'isActive', 'countJSON', 'isActiveJSON'].forEach((key) => {
  localStorage.removeItem(key);
});

/**
 * ========================================
 * EXAMPLE 6: Storage Helper Class
 * ========================================
 */
console.log('\n--- Example 6: Storage Helper Class ---');

class Storage {
  constructor(prefix = 'app_') {
    this.prefix = prefix;
  }

  _getKey(key) {
    return `${this.prefix}${key}`;
  }

  set(key, value) {
    const data = JSON.stringify(value);
    localStorage.setItem(this._getKey(key), data);
  }

  get(key, defaultValue = null) {
    const data = localStorage.getItem(this._getKey(key));
    if (data === null) return defaultValue;

    try {
      return JSON.parse(data);
    } catch {
      return data;
    }
  }

  remove(key) {
    localStorage.removeItem(this._getKey(key));
  }

  has(key) {
    return localStorage.getItem(this._getKey(key)) !== null;
  }

  clear() {
    // Only clear items with our prefix
    const keysToRemove = [];
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key.startsWith(this.prefix)) {
        keysToRemove.push(key);
      }
    }
    keysToRemove.forEach((key) => localStorage.removeItem(key));
  }
}

// Usage
const storage = new Storage('myApp_');

storage.set('user', { name: 'John', age: 30 });
storage.set('settings', { theme: 'dark', fontSize: 16 });
storage.set('isLoggedIn', true);

console.log('User:', storage.get('user'));
console.log('Settings:', storage.get('settings'));
console.log('Is logged in:', storage.get('isLoggedIn'));
console.log('Missing key:', storage.get('missing', 'default'));
console.log('Has user:', storage.has('user'));

// Clean up
storage.clear();

/**
 * ========================================
 * EXAMPLE 7: Storage with Expiration
 * ========================================
 */
console.log('\n--- Example 7: Storage with Expiration ---');

class ExpiringStorage {
  static set(key, value, ttlSeconds) {
    const item = {
      value: value,
      expiry: Date.now() + ttlSeconds * 1000,
      createdAt: Date.now(),
    };
    localStorage.setItem(key, JSON.stringify(item));
  }

  static get(key) {
    const itemStr = localStorage.getItem(key);
    if (!itemStr) return null;

    try {
      const item = JSON.parse(itemStr);

      if (Date.now() > item.expiry) {
        localStorage.removeItem(key);
        console.log(`Key "${key}" expired and was removed`);
        return null;
      }

      return item.value;
    } catch {
      return null;
    }
  }

  static getTimeRemaining(key) {
    const itemStr = localStorage.getItem(key);
    if (!itemStr) return null;

    try {
      const item = JSON.parse(itemStr);
      const remaining = item.expiry - Date.now();
      return remaining > 0 ? remaining : 0;
    } catch {
      return null;
    }
  }
}

// Store item that expires in 5 seconds
ExpiringStorage.set('tempToken', 'abc123', 5);

console.log('Token:', ExpiringStorage.get('tempToken'));
console.log(
  'Time remaining:',
  ExpiringStorage.getTimeRemaining('tempToken'),
  'ms'
);

// Demonstrate expiration (uncomment to test)
// setTimeout(() => {
//     console.log('After 6 seconds:', ExpiringStorage.get('tempToken')); // null
// }, 6000);

/**
 * ========================================
 * EXAMPLE 8: Checking Storage Availability
 * ========================================
 */
console.log('\n--- Example 8: Checking Storage Availability ---');

function isStorageAvailable(type) {
  let storage;
  try {
    storage = window[type];
    const testKey = '__storage_test__';
    storage.setItem(testKey, testKey);
    storage.removeItem(testKey);
    return true;
  } catch (e) {
    return (
      e instanceof DOMException &&
      // Everything except Firefox
      (e.code === 22 ||
        // Firefox
        e.code === 1014 ||
        // Test name field too, because code might not be present
        e.name === 'QuotaExceededError' ||
        e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
      // Acknowledge QuotaExceededError only if there's something already stored
      storage &&
      storage.length !== 0
    );
  }
}

console.log('localStorage available:', isStorageAvailable('localStorage'));
console.log('sessionStorage available:', isStorageAvailable('sessionStorage'));

/**
 * ========================================
 * EXAMPLE 9: Storage Size Calculator
 * ========================================
 */
console.log('\n--- Example 9: Storage Size Calculator ---');

function getStorageSize(storage = localStorage) {
  let total = 0;

  for (let i = 0; i < storage.length; i++) {
    const key = storage.key(i);
    const value = storage.getItem(key);
    // Each character is 2 bytes in JavaScript (UTF-16)
    total += (key.length + value.length) * 2;
  }

  return {
    bytes: total,
    kilobytes: (total / 1024).toFixed(2),
    megabytes: (total / (1024 * 1024)).toFixed(4),
  };
}

// Add some test data
localStorage.setItem('sizeTest1', 'Hello World');
localStorage.setItem('sizeTest2', JSON.stringify({ data: 'x'.repeat(1000) }));

const size = getStorageSize();
console.log('Storage size:', size);

// Clean up
localStorage.removeItem('sizeTest1');
localStorage.removeItem('sizeTest2');

/**
 * ========================================
 * EXAMPLE 10: Iterating Over Storage
 * ========================================
 */
console.log('\n--- Example 10: Iterating Over Storage ---');

// Add test data
localStorage.setItem('iter_name', 'John');
localStorage.setItem('iter_age', '30');
localStorage.setItem('iter_city', 'NYC');

// Method 1: Using key() and length
console.log('Method 1 - key() and length:');
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  if (key.startsWith('iter_')) {
    console.log(`  ${key}: ${localStorage.getItem(key)}`);
  }
}

// Method 2: Using Object.keys()
console.log('Method 2 - Object.keys():');
Object.keys(localStorage)
  .filter((key) => key.startsWith('iter_'))
  .forEach((key) => {
    console.log(`  ${key}: ${localStorage.getItem(key)}`);
  });

// Method 3: Get all as object
console.log('Method 3 - As object:');
const allData = {};
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  if (key.startsWith('iter_')) {
    allData[key] = localStorage.getItem(key);
  }
}
console.log('  ', allData);

// Clean up
['iter_name', 'iter_age', 'iter_city'].forEach((key) =>
  localStorage.removeItem(key)
);

/**
 * ========================================
 * EXAMPLE 11: Storage Event Listener
 * ========================================
 */
console.log('\n--- Example 11: Storage Event Listener ---');

// This event fires when storage changes in ANOTHER tab
function handleStorageChange(event) {
  console.log('Storage changed!');
  console.log('Key:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('URL:', event.url);
  console.log(
    'Storage area:',
    event.storageArea === localStorage ? 'localStorage' : 'sessionStorage'
  );
}

// Add listener
window.addEventListener('storage', handleStorageChange);

console.log('Storage event listener added.');
console.log(
  'Open this page in another tab and modify localStorage to see events.'
);

// To test:
// 1. Open this page in two tabs
// 2. In one tab's console, run: localStorage.setItem('test', 'hello')
// 3. The other tab will log the storage event

/**
 * ========================================
 * EXAMPLE 12: Todo List with Storage
 * ========================================
 */
console.log('\n--- Example 12: Todo List with Storage ---');

class TodoStorage {
  constructor(storageKey = 'todos') {
    this.storageKey = storageKey;
  }

  _load() {
    const data = localStorage.getItem(this.storageKey);
    return data ? JSON.parse(data) : [];
  }

  _save(todos) {
    localStorage.setItem(this.storageKey, JSON.stringify(todos));
  }

  getAll() {
    return this._load();
  }

  add(text) {
    const todos = this._load();
    const newTodo = {
      id: Date.now(),
      text: text,
      completed: false,
      createdAt: new Date().toISOString(),
    };
    todos.push(newTodo);
    this._save(todos);
    return newTodo;
  }

  toggle(id) {
    const todos = this._load();
    const todo = todos.find((t) => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
      this._save(todos);
    }
    return todo;
  }

  remove(id) {
    const todos = this._load();
    const filtered = todos.filter((t) => t.id !== id);
    this._save(filtered);
    return filtered;
  }

  clear() {
    this._save([]);
  }
}

// Usage
const todoStore = new TodoStorage('demo_todos');
todoStore.clear();

console.log('Adding todos...');
const todo1 = todoStore.add('Learn localStorage');
const todo2 = todoStore.add('Build a project');
const todo3 = todoStore.add('Practice daily');

console.log('All todos:', todoStore.getAll());

console.log('Toggling first todo...');
todoStore.toggle(todo1.id);
console.log('After toggle:', todoStore.getAll());

console.log('Removing second todo...');
todoStore.remove(todo2.id);
console.log('After remove:', todoStore.getAll());

// Clean up
localStorage.removeItem('demo_todos');

/**
 * ========================================
 * EXAMPLE 13: User Preferences Manager
 * ========================================
 */
console.log('\n--- Example 13: User Preferences Manager ---');

class PreferencesManager {
  constructor() {
    this.storageKey = 'userPreferences';
    this.defaults = {
      theme: 'light',
      fontSize: 16,
      language: 'en',
      notifications: true,
      autoSave: true,
    };
  }

  get() {
    const saved = localStorage.getItem(this.storageKey);
    if (!saved) return { ...this.defaults };

    return { ...this.defaults, ...JSON.parse(saved) };
  }

  set(key, value) {
    const prefs = this.get();
    prefs[key] = value;
    localStorage.setItem(this.storageKey, JSON.stringify(prefs));
    return prefs;
  }

  setMultiple(updates) {
    const prefs = this.get();
    Object.assign(prefs, updates);
    localStorage.setItem(this.storageKey, JSON.stringify(prefs));
    return prefs;
  }

  reset() {
    localStorage.removeItem(this.storageKey);
    return this.defaults;
  }
}

// Usage
const prefs = new PreferencesManager();

console.log('Default preferences:', prefs.get());

prefs.set('theme', 'dark');
console.log('After setting theme:', prefs.get());

prefs.setMultiple({ fontSize: 18, language: 'es' });
console.log('After bulk update:', prefs.get());

prefs.reset();
console.log('After reset:', prefs.get());

/**
 * ========================================
 * EXAMPLE 14: Session-based Form Draft
 * ========================================
 */
console.log('\n--- Example 14: Session-based Form Draft ---');

class FormDraft {
  constructor(formId) {
    this.formId = formId;
    this.storageKey = `formDraft_${formId}`;
  }

  save(formData) {
    const draft = {
      data: formData,
      savedAt: new Date().toISOString(),
    };
    sessionStorage.setItem(this.storageKey, JSON.stringify(draft));
    console.log('Draft saved at', draft.savedAt);
  }

  load() {
    const saved = sessionStorage.getItem(this.storageKey);
    if (!saved) return null;

    const draft = JSON.parse(saved);
    console.log('Draft loaded from', draft.savedAt);
    return draft.data;
  }

  clear() {
    sessionStorage.removeItem(this.storageKey);
    console.log('Draft cleared');
  }

  hasDraft() {
    return sessionStorage.getItem(this.storageKey) !== null;
  }
}

// Usage
const contactFormDraft = new FormDraft('contactForm');

// Simulate saving form data
contactFormDraft.save({
  name: 'John Doe',
  email: 'john@example.com',
  message: 'Hello, I would like to...',
});

// Check if draft exists
console.log('Has draft:', contactFormDraft.hasDraft());

// Load draft
const savedData = contactFormDraft.load();
console.log('Loaded draft:', savedData);

// Clear draft (e.g., after successful submit)
contactFormDraft.clear();

/**
 * ========================================
 * EXAMPLE 15: Cross-Tab State Sync
 * ========================================
 */
console.log('\n--- Example 15: Cross-Tab State Sync ---');

class CrossTabSync {
  constructor(channelName) {
    this.channelName = channelName;
    this.listeners = [];

    // Listen for storage events from other tabs
    window.addEventListener('storage', (e) => {
      if (e.key === `sync_${this.channelName}`) {
        const message = JSON.parse(e.newValue);
        this._notifyListeners(message);
      }
    });
  }

  broadcast(message) {
    const data = {
      ...message,
      timestamp: Date.now(),
      tabId: this._getTabId(),
    };

    // Store the message (triggers storage event in other tabs)
    localStorage.setItem(`sync_${this.channelName}`, JSON.stringify(data));

    // Clean up after a short delay
    setTimeout(() => {
      localStorage.removeItem(`sync_${this.channelName}`);
    }, 100);
  }

  onMessage(callback) {
    this.listeners.push(callback);
  }

  _notifyListeners(message) {
    this.listeners.forEach((callback) => callback(message));
  }

  _getTabId() {
    if (!sessionStorage.getItem('tabId')) {
      sessionStorage.setItem('tabId', Math.random().toString(36).substr(2, 9));
    }
    return sessionStorage.getItem('tabId');
  }
}

// Usage
const sync = new CrossTabSync('app-events');

sync.onMessage((message) => {
  console.log('Received from another tab:', message);
});

// To test:
// Open page in multiple tabs
// In one tab: sync.broadcast({ type: 'logout' })
// Other tabs will receive the message

console.log('Cross-tab sync initialized.');
console.log('Open in another tab and run: sync.broadcast({ type: "hello" })');

/**
 * ========================================
 * Running Examples Summary
 * ========================================
 */
console.log('\n========================================');
console.log('localStorage and sessionStorage Examples');
console.log('========================================');
console.log('All examples completed!');
console.log('');
console.log('Key classes available:');
console.log('- Storage: Namespaced storage with JSON support');
console.log('- ExpiringStorage: Storage with TTL');
console.log('- TodoStorage: Todo list with persistence');
console.log('- PreferencesManager: User preferences handler');
console.log('- FormDraft: Session-based form draft');
console.log('- CrossTabSync: Cross-tab communication');
Examples - JavaScript Tutorial | DeepML