Concept
"HTML5 APIs" is a loose umbrella term for the batch of JavaScript-accessible browser capabilities that shipped alongside the HTML5 spec effort, most of them aren't part of the HTML markup language itself, they're window/DOM-exposed JS APIs that happened to arrive in the same era. Knowing what exists (and reaching for the platform before a library) is most of the value here; each API below gets more depth in its own topic where warranted.
The Observer family, replacing expensive polling/event listeners
Before Observers, watching for "did this element enter the viewport" or "did this element resize" meant listening to scroll/resize on window and manually calling getBoundingClientRect() on every event, expensive, layout-thrashing, and imprecise.
// IntersectionObserver, is this element visible in the viewport?
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible"); // lazy-load, animate in, infinite scroll trigger
}
});
}, { threshold: 0.1 });
observer.observe(document.querySelector(".lazy-image"));// ResizeObserver, did this specific element's size change? (not just window resize)
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
console.log(entry.contentRect.width, entry.contentRect.height);
}
});
resizeObserver.observe(document.querySelector(".resizable-panel"));// MutationObserver, watch for DOM changes (attribute, child list, text) without polling
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((m) => console.log(m.type, m.target));
});
mutationObserver.observe(document.body, { childList: true, subtree: true, attributes: true });All three run asynchronously off the main thread's critical rendering path (batched, not synchronous per-frame), which is why they're the correct tool over manual scroll/resize listeners or setInterval polling for these use cases, they're both more accurate and meaningfully cheaper.
Geolocation
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position.coords.latitude, position.coords.longitude);
},
(error) => console.error(error.message),
{ enableHighAccuracy: true, timeout: 5000 }
);
// Or watch continuously (e.g. live map tracking)
const watchId = navigator.geolocation.watchPosition(onSuccess, onError);
navigator.geolocation.clearWatch(watchId);Requires an explicit user permission prompt (and HTTPS, Geolocation is a "powerful feature" restricted to secure contexts). Always handle the error/denial case; a meaningful fraction of users decline the prompt.
Web Workers, real parallelism for JS
// main.js
const worker = new Worker("worker.js");
worker.postMessage({ command: "process", data: largeArray });
worker.onmessage = (e) => console.log("Result:", e.data);
// worker.js, runs on a separate thread, no DOM access
self.onmessage = (e) => {
const result = heavyComputation(e.data.data);
self.postMessage(result);
};JavaScript is single-threaded on the main thread by default, any expensive synchronous computation (large data processing, image manipulation, complex calculations) blocks rendering and input handling. Web Workers run on an actual separate OS thread with no DOM access (that's the fundamental tradeoff, communication only happens via postMessage, structured-cloned, not shared references), making them the correct tool for genuinely CPU-heavy work that doesn't need to touch the page directly.
Drag and Drop
<div draggable="true" id="drag-me">Drag me</div>
<div id="drop-zone">Drop here</div>document.getElementById("drag-me").addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", "some-id");
});
document.getElementById("drop-zone").addEventListener("dragover", (e) => {
e.preventDefault(); // required, dragover is the "allow drop" signal
});
document.getElementById("drop-zone").addEventListener("drop", (e) => {
e.preventDefault();
const data = e.dataTransfer.
The native Drag and Drop API is notoriously fiddly (inconsistent default styling, awkward touch-device support, easy to forget preventDefault() on dragover which silently disables dropping), most production drag-and-drop UIs (Trello-style boards, file reorder lists) use a library (dnd-kit, react-beautiful-dnd's successors) built on top of pointer events rather than the native API directly, specifically because of these rough edges. Worth knowing the native API exists and how it works, but it's a reasonable case where reaching for a library is the pragmatic choice.
File API
<input type="file" id="file-input" accept="image/*" multiple />document.getElementById("file-input").addEventListener("change", (e) => {
for (const file of e.target.files) {
console.log(file.name, file.size, file.type);
const reader = new FileReader();
reader.onload = (event) => {
previewImg.src = event.target.result; // data: URL, for instant preview
};
reader.readAsDataURL(file);
}
});FileReader reads file contents asynchronously (as text, data URL, or ArrayBuffer) without ever uploading anything, purely local, client-side. This is what powers instant image preview before upload, or client-side CSV/text parsing.
Other notable ones
- Fullscreen API (
element.requestFullscreen()), used by video players, presentation apps. - Page Visibility API (
document.visibilityState,visibilitychangeevent), pause video/animations/polling when the tab isn't visible, a genuinely easy win for battery/bandwidth that's often skipped. - Clipboard API (
navigator.clipboard.writeText()), modern replacement for the olddocument.execCommand('copy')hack. - Notifications API (
new Notification(...), requires permission), OS-level notifications from a web page. requestIdleCallback, schedule non-urgent work for when the browser is otherwise idle, so it doesn't compete with rendering.
Common Mistakes
1. Polling with setInterval for things Observers solve better
// Wrong: expensive, imprecise, runs even when nothing changed
setInterval(() => {
const rect = element.getBoundingClientRect();
if (rect.top < window.innerHeight) loadImage();
}, 100);IntersectionObserver does this correctly, asynchronously, and far more cheaply, reach for the Observer family before a polling loop for visibility/size/mutation detection.
2. Forgetting preventDefault() on dragover
Without it, the browser's default behavior (which does not allow drop) wins, and the drop event never fires, a very common "drag and drop silently doesn't work" bug with no console error.
3. Trying to touch the DOM from inside a Web Worker
Workers have zero access to document/window/the DOM, attempting it throws a ReferenceError. All communication with the main thread must go through postMessage, and only structured-cloneable data (no functions, no DOM nodes) can be passed.
4. Not handling Geolocation permission denial
// Wrong: assumes success is the only outcome
navigator.geolocation.getCurrentPosition((position) => { /* ... */ });Always pass an error callback, a meaningful fraction of users deny the permission prompt, and the feature should degrade gracefully (manual location entry, or a clear "location access needed" message), not fail silently or throw an uncaught error.
5. Reaching for a heavy library before checking if the platform already does it
Drag-and-drop is a legitimate case for a library, but plenty of "we need a library for this" instincts (intersection detection, clipboard copy, fullscreen) are now solved natively and don't need one.
Best Practices
- Reach for
IntersectionObserver/ResizeObserver/MutationObserverbeforescroll/resizelisteners or polling for their respective use cases. - Always handle the permission-denial/error path for Geolocation, Notifications, and Clipboard, these are user-permission-gated and denial is a normal, expected outcome.
- Use Web Workers for genuinely CPU-heavy synchronous work that would otherwise block the main thread, not for I/O-bound async work (
fetchis already non-blocking without a worker). document.visibilityStateto pause expensive background work (video, polling, animation) when the tab is hidden.- , most of these APIs are restricted to secure contexts by spec.
Further Resources
- MDN, Web APIs index
- MDN, Intersection Observer API
- MDN, Web Workers API
- MDN, HTML Drag and Drop API
- MDN, File API
- Can I Use, check browser support before relying on any newer API.
