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

  1. Request permissions in context — explain why before Geolocation/Notifications
  2. Abort fetch on navigation with AbortController
  3. Prefer observers over scroll/resize polling
  4. Graceful degradation when APIs unavailable
  5. 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.