Web APIs Deep Dive
Beyond the DOM
The browser platform exposes dozens of Web APIs beyond core JavaScript — networking, storage, observers, device access, and performance tools. Understanding these APIs unlocks production-grade frontend development without third-party libraries.
Fetch API (Advanced)
async function apiRequest(url, options = {}) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...options.headers
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
return await response.json();
}
return await response.text();
} finally {
clearTimeout(timeout);
}
}
Upload with Progress
function uploadFile(url, file, onProgress) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) onProgress(e.loaded / e.total);
});
xhr.addEventListener('load', () => resolve(xhr.response));
xhr.addEventListener('error', reject);
xhr.open('POST', url);
xhr.send(file);
});
}
Fetch doesn’t support upload progress — use XHR or streams for that.
Intersection Observer
Detect when elements enter/leave the viewport — lazy loading, infinite scroll, analytics:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
}, {
root: null, // viewport
rootMargin: '100px', // preload before visible
threshold: 0.1 // 10% visible triggers
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
More efficient than scroll event listeners — runs off main thread.
MutationObserver
Watch DOM tree changes — useful for third-party widgets or testing:
const mo = new MutationObserver((mutations) => {
mutations.forEach(m => {
if (m.type === 'childList') {
console.log('Nodes added:', m.addedNodes);
}
if (m.type === 'attributes') {
console.log('Attribute changed:', m.attributeName);
}
});
});
mo.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'data-state']
});
// mo.disconnect() when done
ResizeObserver
React to element size changes (not just window):
const ro = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
chart.resize(width, height);
}
});
ro.observe(document.querySelector('#chart-container'));
Powers container queries polyfills and responsive components.
Geolocation API
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
console.log(latitude, longitude);
},
(error) => {
console.error(error.code, error.message);
},
{ enableHighAccuracy: true, timeout: 5000, maximumAge: 60000 }
);
}
Requires HTTPS and user permission. Handle denial gracefully.
Clipboard API
async function copyToClipboard(text) {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
} else {
// Fallback
const ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
}
}
Requires secure context (HTTPS) and may need user gesture.
Web Notifications
async function notify(title, body) {
if (!('Notification' in window)) return;
const permission = await Notification.requestPermission();
if (permission === 'granted') {
new Notification(title, { body, icon: '/icon.png' });
}
}
Page Visibility API
Pause work when tab is hidden:
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
pauseVideo();
stopPolling();
} else {
resumeVideo();
startPolling();
}
});
Performance APIs
// High-resolution timing
performance.mark('start-render');
renderChart();
performance.mark('end-render');
performance.measure('chart-render', 'start-render', 'end-render');
const measure = performance.getEntriesByName('chart-render')[0];
console.log(measure.duration, 'ms');
// Navigation timing
const nav = performance.getEntriesByType('navigation')[0];
console.log('DOM loaded:', nav.domContentLoadedEventEnd);
Structured Clone and postMessage
// Workers and windows
worker.postMessage(largeObject); // structured clone, not JSON
window.addEventListener('message', (e) => {
if (e.origin !== 'https://trusted.example.com') return;
console.log(e.data);
});
Always validate origin on incoming messages.
Feature Detection
if ('IntersectionObserver' in window) {
initLazyLoad();
} else {
loadAllImages(); // fallback
}
// Or use dynamic import
if (navigator.share) {
showNativeShareButton();
}
Detect features, not browsers — "geolocation" in navigator not user-agent sniffing.
Best Practices
- Request permissions in context — explain why before Geolocation/Notifications
- Abort fetch on navigation with AbortController
- Prefer observers over scroll/resize polling
- Graceful degradation when APIs unavailable
- Secure context — many APIs require HTTPS
Troubleshooting
Fetch CORS error
- Server must send
Access-Control-Allow-Origin; proxy in development
Observer callback not firing
- Check thresholds, root element, and that target is in DOM
Clipboard denied
- Requires user gesture; check permissions policy
Web APIs turn the browser into a rich application platform — learn them to build modern, performant experiences.