Page Visibility API is a browser API that reports whether a page is visible to the user through document.visibilityState and the visibilitychange event. It has only two states, "visible" and "hidden". It is now specified in the HTML Standard, because the standalone W3C Page Visibility Level 2 document was discontinued on 23 June 2022.
The visibility state belongs to the top-level document. The spec says the document is "hidden" when it cannot be seen on any screen. Its examples are a background tab, a minimized browser, a browser moved off the screen, and an operating system lock screen covering the browser. The state is "visible" when any part of the viewport contents can be seen. A browser may also report "visible" while assistive technology is attached, even if the window would otherwise count as hidden.
A new Document starts with the state "hidden". The API has three members:
When the state changes the browser queues a task that fires the event. Other specifications hook into the same step, and the HTML Standard names Web NFC and the Device Posture API as examples. When a document is being unloaded, the hidden steps run during the unload itself rather than in a later task, so visibilitychange is the last reliable signal a page gets before it goes away.
The sample uses a stand-in document built from EventTarget, so the logic runs in Node.
const doc = new EventTarget();
doc.visibilityState = "visible";
Object.defineProperty(doc, "hidden", { get() { return this.visibilityState === "hidden"; } });
const log = [];
doc.addEventListener("visibilitychange", () => {
log.push(`${doc.visibilityState} (hidden=${doc.hidden}) -> ` + (doc.hidden ? "pause video, flush analytics" : "resume video"));
});
for (const s of ["hidden", "visible", "hidden"]) { doc.visibilityState = s; doc.dispatchEvent(new Event("visibilitychange")); }
console.log(log.join("\n"));
Output:
hidden (hidden=true) -> pause video, flush analytics
visible (hidden=false) -> resume video
hidden (hidden=true) -> pause video, flush analytics
visibilitychange tracks whether the page can be seen, while blur tracks keyboard focus. A window placed beside another one loses focus and fires blur, but its visibilityState stays "visible" because some of the viewport is still showing. Use focus and blur for input-focus logic and visibility for pausing media or polling.
All current engines support it. MDN compatibility data lists document.visibilityState and document.hidden from Chrome 33, Firefox 18, Safari 7 and Edge 12, and the visibilitychange event from Chrome 62, Firefox 56, Safari 14.1 and Edge 18.