Page Visibility API
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list no automated tools; the code pattern (visibilitychange|document.hidden) requires manual inspection or integration testing to catch misuse. Linters won't flag blur/focus being used instead of visibilitychange.
Closest to 'simple parameterised fix' (e3). The quick_fix is straightforward: replace blur/focus handlers with visibilitychange listener and add document.hidden checks. This typically involves rewriting a single component's event handlers but doesn't require cross-file refactoring.
Closest to 'localised tax' (b3). Page Visibility API adoption is isolated to components that perform background work (animations, polls, WebSockets). Once implemented, it doesn't shape the rest of the codebase; other features remain unaffected. The choice doesn't impose architectural constraints.
Closest to 'notable trap' (t5). The misconception field explicitly documents the documented gotcha: developers conflate blur/focus with tab visibility. This is a well-known trap that most developers eventually encounter and learn, but it's not immediately obvious from the API naming alone.
TL;DR
Explanation
document.visibilityState is 'visible' or 'hidden'. The visibilitychange event fires when it changes. Use cases: pause autoplay video when tab hides, stop polling when hidden (save bandwidth/battery), pause Canvas animations, pause WebSocket reconnection attempts. document.hidden is a boolean shorthand. The API fires reliably on tab switch, minimise, and screen lock. On mobile, it fires on home button press. Page Visibility should be the primary trigger for any 'pause when not visible' logic.
Common Misconception
Why It Matters
Common Mistakes
- Using blur/focus events instead of visibilitychange for tab-switch detection.
- Not pausing WebSocket reconnection when page is hidden — wastes connections.
- Not resuming state when page becomes visible again.
Code Examples
// Always polling regardless of tab visibility:
setInterval(fetchUpdates, 5000);
let pollInterval;
function startPolling() {
pollInterval = setInterval(fetchUpdates, 5000);
}
function stopPolling() {
clearInterval(pollInterval);
}
document.addEventListener('visibilitychange', () => {
document.hidden ? stopPolling() : startPolling();
});
startPolling(); // Start initially