Event Listeners
StorageManager.js allows you to listen for changes to specific keys across different tabs or windows. This is useful for keeping your application synchronized when a user has multiple tabs open.
How It Works
Section titled “How It Works”The event listener system is built on top of the browser’s native storage event, which fires when storage is modified in a different browsing context (tab or window) on the same domain.
onChange()
Section titled “onChange()”Registers a callback function to be executed when a specific key changes in storage.
Syntax
Section titled “Syntax”storage.onChange(key, callback)Parameters
Section titled “Parameters”key(String) — The key to listen for changes oncallback(Function) — A function called when the key changes. Receives:newValue(Any) — The new value (parsed from JSON)oldValue(Any) — The previous value (parsed from JSON)
Returns
Section titled “Returns”void (synchronous method)
Example
Section titled “Example”// In Tab 1: Register a listenerstorage.onChange('userProfile', (newValue, oldValue) => { console.log('User profile changed!'); console.log('Old value:', oldValue); console.log('New value:', newValue);
// Update UI with new values updateUI(newValue);});
// In Tab 2: Change the valueawait storage.set('userProfile', { name: 'Bob', theme: 'light'});
// Tab 1's callback is triggered:// "User profile changed!"// "Old value: { name: 'Alice', theme: 'dark' }"// "New value: { name: 'Bob', theme: 'light' }"Use Cases
Section titled “Use Cases”- Synchronizing user preferences across tabs
- Showing notifications when data changes in another tab
- Updating a shopping cart when items are added/removed in another tab
- Real-time collaboration features
- Keeping dashboards in sync
offChange()
Section titled “offChange()”Unregisters the change listener for a specific key.
Syntax
Section titled “Syntax”storage.offChange(key)Parameters
Section titled “Parameters”key(String) — The key to stop listening for changes on
Returns
Section titled “Returns”void (synchronous method)
Example
Section titled “Example”// Register a listenerstorage.onChange('notifications', (newValue) => { showNotification(newValue);});
// Later: Stop listeningstorage.offChange('notifications');
// Changes to 'notifications' will no longer trigger the callbackComplete Example: Multi-Tab Sync
Section titled “Complete Example: Multi-Tab Sync”Here’s a complete example of synchronizing state across multiple tabs:
Tab 1: Main Application
Section titled “Tab 1: Main Application”const storage = new StorageManager(false, { namespace: 'myApp'});
// Initialize the UI with stored dataconst cart = await storage.get('cart') || [];renderCart(cart);
// Listen for cart changes from other tabsstorage.onChange('cart', (newCart) => { console.log('Cart updated in another tab'); renderCart(newCart); showNotification('Your cart was updated');});
// Function to render cart itemsfunction renderCart(items) { const cartElement = document.getElementById('cart'); cartElement.innerHTML = items.map(item => `<div>${item.name} - $${item.price}</div>` ).join('');}
function showNotification(message) { // Show a toast/notification console.log('Notification:', message);}Tab 2: Product Page
Section titled “Tab 2: Product Page”const storage = new StorageManager(false, { namespace: 'myApp'});
// Add item to cartasync function addToCart(item) { const cart = await storage.get('cart') || []; cart.push(item); await storage.set('cart', cart);
console.log('Item added to cart'); // Tab 1 will automatically be notified and update its UI}
// User clicks "Add to Cart"document.getElementById('addBtn').addEventListener('click', () => { addToCart({ id: 123, name: 'Widget', price: 29.99 });});Advanced Example: Real-Time Preferences
Section titled “Advanced Example: Real-Time Preferences”Sync user preferences across all open tabs:
const storage = new StorageManager(false, { namespace: 'myApp'});
// Initialize theme from storageconst savedTheme = await storage.get('theme') || 'light';applyTheme(savedTheme);
// Listen for theme changesstorage.onChange('theme', (newTheme) => { console.log(`Theme changed to ${newTheme} in another tab`); applyTheme(newTheme);});
// Theme toggle buttondocument.getElementById('themeToggle').addEventListener('click', async () => { const currentTheme = await storage.get('theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light';
await storage.set('theme', newTheme); applyTheme(newTheme); // All other tabs will automatically update their theme});
function applyTheme(theme) { document.body.className = theme; document.getElementById('themeToggle').textContent = theme === 'light' ? '🌙 Dark Mode' : '☀️ Light Mode';}
// Clean up listener when page unloadswindow.addEventListener('beforeunload', () => { storage.offChange('theme');});Multiple Listeners
Section titled “Multiple Listeners”You can register listeners for multiple keys in the same instance:
// Listen for user profile changesstorage.onChange('userProfile', (newProfile) => { updateUserDisplay(newProfile);});
// Listen for settings changesstorage.onChange('settings', (newSettings) => { applySettings(newSettings);});
// Listen for notificationsstorage.onChange('notifications', (notifications) => { showNotifications(notifications);});
// Later: Clean up all listenersstorage.offChange('userProfile');storage.offChange('settings');storage.offChange('notifications');Limitations
Section titled “Limitations”- Cross-tab only: The
storageevent only fires in tabs other than the one that made the change. - Same origin: Tabs must be on the same domain/protocol/port.
- Browser support: All modern browsers support the storage event (IE8+).
Best Practices
Section titled “Best Practices”- Clean up listeners: Always call
offChange()when you’re done listening - Handle null values: Check for null in callbacks (key might have been removed)
- Debounce rapid changes: If you expect frequent updates, debounce your callback
- Validate data: Always validate data in callbacks before using it
// Good: Clean up and validatestorage.onChange('user', (newUser) => { if (!newUser) { console.log('User logged out in another tab'); redirectToLogin(); return; }
updateUserDisplay(newUser);});
window.addEventListener('beforeunload', () => { storage.offChange('user');});Next Steps
Section titled “Next Steps”Explore practical Examples to see all these features working together in real applications.