Example: Web Storage operations with JSON serialization
// Store simple values (localStorage)localStorage.setItem('username', 'john_doe');localStorage.setItem('theme', 'dark');// Retrieve valuesconst username = localStorage.getItem('username'); // "john_doe"const theme = localStorage.getItem('theme'); // "dark"// Store complex objects (must stringify)const user = { id: 123, name: 'John Doe', preferences: { theme: 'dark', lang: 'en' }};localStorage.setItem('user', JSON.stringify(user));// Retrieve and parse objectsconst storedUser = JSON.parse(localStorage.getItem('user'));console.log(storedUser.preferences.theme); // "dark"// Remove specific itemlocalStorage.removeItem('theme');// Clear all storagelocalStorage.clear();// sessionStorage (same API, different lifetime)sessionStorage.setItem('tempData', 'session-only');// Iterate through all itemsfor (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); console.log(key, value);}// Storage event listener (fires on other tabs/windows)window.addEventListener('storage', (e) => { console.log('Key:', e.key); console.log('Old value:', e.oldValue); console.log('New value:', e.newValue); console.log('URL:', e.url); console.log('Storage area:', e.storageArea);});
Security Warning: Never store sensitive data (passwords, credit cards, tokens) in
localStorage/sessionStorage - it's accessible via JavaScript and vulnerable to XSS attacks. Data is stored as
plain text. Use httpOnly cookies or secure server-side sessions for sensitive information.
Best Practices: Always use try-catch for storage operations (can fail if quota exceeded or
disabled). Use JSON for objects. Implement versioning for data schema changes. Clear old data periodically.
Consider IndexedDB for large datasets (>10MB). Use compression for large strings.
Example: Geolocation with error handling and options
// Check if geolocation is supportedif ('geolocation' in navigator) { // Get current position (one-time) navigator.geolocation.getCurrentPosition( // Success callback (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; const accuracy = position.coords.accuracy; console.log(`Location: ${lat}, ${lng}`); console.log(`Accuracy: ${accuracy} meters`); // Use coordinates (e.g., show on map) showOnMap(lat, lng); }, // Error callback (error) => { switch(error.code) { case error.PERMISSION_DENIED: console.error('User denied geolocation'); break; case error.POSITION_UNAVAILABLE: console.error('Location unavailable'); break; case error.TIMEOUT: console.error('Request timeout'); break; default: console.error('Unknown error'); } }, // Options { enableHighAccuracy: true, // Use GPS timeout: 5000, // 5 second timeout maximumAge: 60000 // Accept 1-minute-old cache } ); // Watch position (continuous monitoring) const watchId = navigator.geolocation.watchPosition( (position) => { updateMap(position.coords.latitude, position.coords.longitude); // Check if device is moving if (position.coords.speed !== null) { console.log(`Speed: ${position.coords.speed} m/s`); } if (position.coords.heading !== null) { console.log(`Heading: ${position.coords.heading}°`); } }, (error) => console.error(error), { enableHighAccuracy: true } ); // Stop watching after 10 seconds setTimeout(() => { navigator.geolocation.clearWatch(watchId); console.log('Stopped watching position'); }, 10000);} else { console.error('Geolocation not supported');}
Browser Support:All modern browsers - Requires HTTPS (except
localhost). Mobile devices generally more accurate than desktops. User must grant permission. GPS
(enableHighAccuracy) drains battery significantly.
Security Note: Always validate file type and size on both client and server. Never trust file
extensions - check MIME type and magic numbers. Sanitize file names before saving. Use virus scanning for user
uploads. Limit file sizes to prevent DoS attacks.
4. History API and Navigation Management
Method
Syntax
Description
Updates URL
pushState()
history.pushState(state, title, url)
Add new history entry (creates new entry in stack)
User navigates (back/forward), or history.back/forward/go() called
event.state contains state object
hashchange
URL hash (#) changes
oldURL, newURL
Example: Single Page Application (SPA) routing with History API
// SPA Router implementationconst routes = { '/': renderHome, '/about': renderAbout, '/contact': renderContact};// Navigate to new page without reloadfunction navigateTo(path) { // Add to history history.pushState({ path }, '', path); // Render new content renderPage(path);}// Render page based on pathfunction renderPage(path) { const render = routes[path] || render404; render(); // Update active nav links document.querySelectorAll('nav a').forEach(link => { link.classList.toggle('active', link.pathname === path); });}// Handle browser back/forwardwindow.addEventListener('popstate', (e) => { const path = e.state?.path || '/'; renderPage(path);});// Intercept link clicksdocument.addEventListener('click', (e) => { if (e.target.matches('a[href^="/"]')) { e.preventDefault(); navigateTo(e.target.pathname); }});// Example: Update URL without navigationfunction updateFilters(filters) { const url = new URL(window.location); url.searchParams.set('filter', filters); // Replace current history entry (no new back button entry) history.replaceState({ filters }, '', url);}// Example: Handle state changeswindow.addEventListener('popstate', (e) => { console.log('State:', e.state); console.log('URL:', location.pathname); if (e.state?.filters) { applyFilters(e.state.filters); }});// Control scroll restorationhistory.scrollRestoration = 'manual'; // Manually handle scroll position// Save scroll position before navigationwindow.addEventListener('beforeunload', () => { const scrollPos = { x: window.scrollX, y: window.scrollY }; history.replaceState({ scrollPos }, '');});// Restore scroll positionwindow.addEventListener('popstate', (e) => { if (e.state?.scrollPos) { window.scrollTo(e.state.scrollPos.x, e.state.scrollPos.y); }});
Important: pushState() and replaceState() don't trigger popstate event. Only user navigation
(back/forward buttons) or history.back/forward/go() trigger popstate. The title parameter is currently ignored
by most browsers. URLs must be same-origin (protocol + domain + port).
5. Web Workers and Shared Workers
Worker Type
Scope
Communication
Use Case
Web Worker (Dedicated)
Single page/script
postMessage (1-to-1)
Heavy computation, data processing
Shared Worker
Multiple pages/tabs (same origin)
postMessage via ports (many-to-1)
Shared state, cross-tab communication
Service Worker
Application-level (all pages)
Events, postMessage
Offline support, caching, push notifications
Worker API
Syntax
Description
new Worker()
const worker = new Worker('worker.js')
Create dedicated worker from script file
postMessage()
worker.postMessage(data)
Send data to worker (structured clone)
onmessage
worker.onmessage = (e) => {}
Receive messages from worker
onerror
worker.onerror = (e) => {}
Handle worker errors
terminate()
worker.terminate()
Immediately stop worker (from main thread)
close()
self.close()
Stop worker (from inside worker)
Worker Context
Available
Not Available
Global Scope
self, importScripts(), navigator, location
window, document, parent
APIs
fetch, setTimeout, setInterval, IndexedDB, Web Crypto
<!-- Main page (main.html) --><script>// Create workerconst worker = new Worker('worker.js');// Send data to workerworker.postMessage({ operation: 'fibonacci', number: 40});// Receive results from workerworker.onmessage = (e) => { console.log('Result from worker:', e.data); // { result: 102334155, time: 1523 }};// Handle worker errorsworker.onerror = (e) => { console.error('Worker error:', e.message); console.error('File:', e.filename, 'Line:', e.lineno);};// Send multiple messagesworker.postMessage({ operation: 'sort', data: [5, 2, 8, 1, 9] });// Terminate worker when donesetTimeout(() => { worker.terminate(); console.log('Worker terminated');}, 5000);</script><!-- Worker file (worker.js) --><script>// Listen for messages from main threadself.onmessage = (e) => { const { operation, number, data } = e.data; const startTime = performance.now(); let result; switch(operation) { case 'fibonacci': result = fibonacci(number); break; case 'sort': result = data.sort((a, b) => a - b); break; default: throw new Error('Unknown operation'); } const time = performance.now() - startTime; // Send result back to main thread self.postMessage({ result, time });};function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}// Import other scriptsimportScripts('math-utils.js', 'helpers.js');// Handle errorsself.onerror = (e) => { console.error('Error in worker:', e);};// Worker can close itself// self.close();</script>
Example: Shared Worker for cross-tab communication
<!-- Main page (any tab) --><script>// Create/connect to shared workerconst sharedWorker = new SharedWorker('shared.js');// Get message portconst port = sharedWorker.port;// Start the portport.start();// Send messageport.postMessage({ type: 'subscribe', channel: 'chat' });// Receive messagesport.onmessage = (e) => { console.log('Message from shared worker:', e.data);};// Send broadcast to all connected tabsport.postMessage({ type: 'broadcast', message: 'Hello from tab ' + Date.now()});</script><!-- Shared Worker (shared.js) --><script>const connections = [];// Handle new connectionsself.onconnect = (e) => { const port = e.ports[0]; connections.push(port); console.log('New connection. Total:', connections.length); // Listen for messages from this port port.onmessage = (e) => { const { type, message, channel } = e.data; if (type === 'broadcast') { // Send to all connected tabs connections.forEach(p => { if (p !== port) { // Don't send back to sender p.postMessage(message); } }); } else if (type === 'subscribe') { port.postMessage({ status: 'subscribed', channel }); } }; // Start the port port.start(); // Send welcome message port.postMessage({ type: 'welcome', connections: connections.length });};</script>
Performance Tips: Workers run on separate threads - perfect for CPU-intensive tasks (parsing,
image processing, data analysis). Use Transferable objects (ArrayBuffer, MessagePort, ImageBitmap) to transfer
large data without copying. Workers have startup cost - reuse workers instead of creating many short-lived ones.
Important Limitations: Service Workers only work over HTTPS (except localhost). Cannot access
DOM directly. Runs on separate thread. Cache storage has quota limits (varies by browser). Must handle cache
versioning carefully to avoid serving stale content.
Section 11 Key Takeaways
localStorage persists across sessions; sessionStorage clears when tab closes - both ~5-10MB limit
Always JSON.stringify/parse for storing objects in Web Storage; use try-catch for quota errors
Geolocation requires HTTPS and user permission; enableHighAccuracy uses GPS (drains battery)
FileReader.readAsDataURL() for image previews; URL.createObjectURL() more efficient for large files
Always validate file type and size on both client and server; never trust file extensions
history.pushState() creates new entry; replaceState() modifies current; only popstate on back/forward
Web Workers run on separate thread - perfect for CPU-intensive tasks; cannot access DOM
Use Transferable objects (ArrayBuffer) to transfer large data without copying between worker threads
Service Workers enable offline support, caching, and push notifications; HTTPS required
Cache strategies: Cache First (static assets), Network First (API), Stale-While-Revalidate (dynamic)