JavaScript Fullscreen API Table

PieceWhat it doesField note
element.requestFullscreen()Go fullscreenReturns a promise - MUST run inside a user gesture (click/tap/key) or the browser rejects it
document.exitFullscreen()Leave fullscreenDocument-level on purpose - whoever is fullscreen, the exit belongs to the page
document.fullscreenElementWho is fullscreenThe element or null - the single state source; per-element boolean flags go stale
document.fullscreenEnabledIs it possible herefalse in iframes without permission or DRM-restricted contexts - check before offering the button
fullscreenchangeThe transition eventFires on ENTER and EXIT (including Escape/system exits) - layout must react here, async after the request
fullscreenerrorThe rejection eventGesture timeout, iframe permission, mid-transition - the event twin of the rejected promise
video.webkitEnterFullscreen()The iPhone pathiOS Safari never shipped standard element fullscreen - prefixed VIDEO-only is what iPhones actually run
Escape exits, alwaysThe browser owns the keyUnblockable by design - react via fullscreenchange (pause video, save state), never fight it
Reference: the MDN Fullscreen API guide. The gesture gate is the API's contract: fullscreen is something the USER does, not something the page does to the user - which is why init-code requests fail and the button lives in your UI. The async trap is the other half: the promise resolving is not the layout changing - measure and resize in fullscreenchange, or you read the old viewport.
Bottom line: read document.fullscreenElement as the single source of truth, because exits come from outside your code (Escape, browser UI, screen lock) and none of them ask permission. Wire the rejection path - requestFullscreen().catch() with a visible fallback - and treat every exit as a pause signal, which is exactly the contract polished video players implement.
Related tools: the picture-in-picture table (the other always-on-top video mode), the screen orientation table (locking landscape once fullscreen), the media session table (OS media keys for the fullscreen player), and the dialog element table (modal layering inside and outside fullscreen).

The Fullscreen API takes one element - a video, a canvas, a map - and stretches it over the entire screen: element.requestFullscreen() in, document.exitFullscreen() out, document.fullscreenElement tells you who is in. It is the video-player button, the game focus mode and the kiosk layout, all with one contract.

Bottom line: fullscreen is GATED on a user gesture - requestFullscreen() outside a click, keypress or touch is rejected by design, which is why the button lives in your UI and not in your init code. The state check is document.fullscreenElement (null = not fullscreen), not a boolean on the element, and the transition is asynchronous - wait for the fullscreenchange event before measuring sizes.

The honest part: iOS Safari - still a major mobile share - never shipped the standard API for arbitrary elements; videos there use webkitEnterFullscreen() on the VIDEO element itself, and webkit-prefixed variants cover older Android and Safari. Cross-browser fullscreen code is one request wrapped in feature checks, or it silently does nothing for a quarter of your users.

How to use

  1. Enter fullscreen on click: button.addEventListener('click', () => stage.requestFullscreen()) - the gesture requirement is non-negotiable, so always trigger from user input.
  2. Toggle safely: document.fullscreenElement ? document.exitFullscreen() : stage.requestFullscreen() - one state source, no stale boolean flags.
  3. React to the transition: document.addEventListener('fullscreenchange', () => resize(stage)) - layout changes ASYNC after the request resolves; measuring immediately reads the old viewport.
  4. Handle rejection: requestFullscreen().catch(() => showFallback()) - iframe without allowfullscreen, gesture timeout and Escape-mid-request all reject; the catch is where your non-fullscreen UX lives.
  5. Cover iOS Safari: if (video.webkitEnterFullscreen) video.webkitEnterFullscreen(); else if (stage.requestFullscreen) stage.requestFullscreen(); - prefixed video-only fullscreen beats nothing on iPhones.

Frequently asked questions

Why does requestFullscreen throw even though the user clicked?

The gesture window is short and strict: the call must happen in the same task as the click (a setTimeout or awaited fetch between click and request loses the gesture), iframes need the allowfullscreen attribute and an allow=fullscreen permission, and some browsers refuse while another fullscreen transition is still settling. The rejection arrives as a rejected promise (or fullscreenerror), so every production player wires the catch to a visible fallback instead of an unhandled rejection in the console.

How do I know if fullscreen is currently active?

document.fullscreenElement - the element currently filling the screen, or null. Per-element flags (element fullscreen or not) go stale because the EXIT can come from outside your code: the Escape key, the browser UI, or the system leaving fullscreen all fire fullscreenchange with fullscreenElement becoming null. Listen to the document-level change event and read fullscreenElement as the single source of truth; document.fullscreenEnabled separately tells you whether fullscreen is POSSIBLE here at all (DRM restrictions, iframe permissions).

Does Escape exit fullscreen, and can I block it?

Escape exits, and you cannot block it - the browser owns that key in fullscreen by design, along with the system gesture on mobile. Your code reacts via fullscreenchange (restore your layout, persist state) rather than preventing the exit. The same applies to window focus loss and screen locks: treat any fullscreen exit as a pause signal for video and games, which is exactly what every polished player does.

What is the cross-browser state of the API in practice?

Standard requestFullscreen/exitFullscreen/fullscreenElement is solid on desktop Chrome, Edge, Firefox and Safari, plus Android Chrome. The gap is iPhone Safari: no standard element fullscreen - videos use the webkit-prefixed method on the VIDEO element (webkitEnterFullscreen, video-only, native controls) and webkitEnterFullScreen spelling variants trap the unwary. Feature-detect in order: standard, webkit video, fallback message - and remember canvas and divs simply cannot go fullscreen on iPhone, so design the mobile layout to not need it.

Related tools