HTML APIs & Web Platform
Web Storage
// localStorage — persists until explicitly cleared (survives browser restarts)
localStorage.setItem('user_prefs', JSON.stringify({ theme: 'dark', lang: 'en' }));
const prefs = JSON.parse(localStorage.getItem('user_prefs') ?? '{}');
localStorage.removeItem('user_prefs');
localStorage.clear(); // clears ALL keys for this origin
// sessionStorage — cleared when tab is closed
sessionStorage.setItem('draft', JSON.stringify(formData));
const draft = JSON.parse(sessionStorage.getItem('draft') ?? 'null');
sessionStorage.removeItem('draft');
// Both have the same API. Key differences:
// localStorage: 5-10MB limit, shared across all tabs for the same origin
// sessionStorage: 5-10MB limit, isolated per tab
// Listen for storage events (fires in OTHER tabs, not the one that changed it)
window.addEventListener('storage', (event) => {
console.log('Key changed:', event.key);
console.log('Old value:', event.oldValue);
console.log('New value:', event.newValue);
});History API
// Navigate without page reload
history.pushState({ page: 'about' }, '', '/about'); // adds to history
history.replaceState({ page: 'home' }, '', '/'); // replaces current entry
// Go back/forward programmatically
history.back();
history.forward();
history.go(-2); // go back 2 steps
// Handle browser back/forward button
window.addEventListener('popstate', (event) => {
console.log('Navigated to state:', event.state);
renderPage(event.state);
});
// Current URL info
const url = new URL(window.location.href);
console.log(url.pathname); // /about
console.log(url.searchParams.get('q')); // query param
url.searchParams.set('page', '2');
history.pushState({}, '', url.toString());Intersection Observer
// Detect when elements enter/leave the viewport
// Use for: lazy loading, infinite scroll, animation triggers, analytics
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
entry.target.src = entry.target.dataset.src; // lazy load image
observer.unobserve(entry.target); // stop observing after first trigger
}
});
}, {
root: null, // viewport
rootMargin: '200px', // start loading 200px before entering viewport
threshold: 0.1, // fire when 10% of element is visible
});
// Observe all lazy images
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
// Infinite scroll
const sentinel = document.querySelector('#load-more');
const infiniteObserver = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) loadMoreItems();
}, { threshold: 1.0 });
infiniteObserver.observe(sentinel);MutationObserver
// Watch for DOM changes (added/removed nodes, attribute changes)
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('Element added:', node.tagName);
}
});
}
if (mutation.type === 'attributes') {
console.log('Attribute changed:', mutation.attributeName);
}
});
});
observer.observe(document.body, {
childList: true, // child node additions/removals
subtree: true, // observe all descendants
attributes: true, // attribute changes
attributeFilter: ['class', 'data-theme'], // only these attributes
characterData: true, // text content changes
});
// Disconnect when done
observer.disconnect();Clipboard API
// Write to clipboard
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast('Copied!');
} catch (err) {
// Fallback for older browsers
const el = document.createElement('textarea');
el.value = text;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
}
// Read from clipboard
async function readFromClipboard() {
const text = await navigator.clipboard.readText();
return text;
}
// Copy image to clipboard
async function copyImage(blob) {
await navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob })
]);
}Drag & Drop API
// Make element draggable
const draggable = document.querySelector('.draggable');
draggable.setAttribute('draggable', 'true');
draggable.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', e.target.id);
e.dataTransfer.effectAllowed = 'move';
});
// Drop target
const dropZone = document.querySelector('.drop-zone');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault(); // allow drop
e.dataTransfer.dropEffect = 'move';
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
const dragged = document.getElementById(id);
dropZone.appendChild(dragged);
dropZone.classList.remove('drag-over');
});Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free