LEARNING OBJECTIVES โต
- Implement robust event listeners for
fullscreenchangeandfullscreenerror. - Accurately query
document.fullscreenElementto synchronize UI controls, icons, and themes. - Understand the event dispatch chain and bubbling behavior across elements and the
Document. - Diagnose and log permission rejections and runtime failures captured by
fullscreenerror. - Architect a resilient state machine to track fullscreen sessions and analytics telemetry.
๐ The Mental Model & Story (Intuitive Foundation)
Imagine an airport air traffic control tower.
Aircraft are constantly taxiing, taking off, climbing, and landing. The air traffic controller doesn't just push a button to grant clearance and then look away; the radar screen continuously tracks which aircraft is currently occupying the active runway.
Whenever an aircraft crosses the runway threshold or clears the landing zone, an automated radar transponder ping sounds across the tower, updating the master status board.
FULLSCREEN EVENT DISPATCH LIFECYCLE
+-------------------------------------------------------------------------------+
| USER ACTION: Clicks "Play Fullscreen" OR Presses [ESC] Key |
+-------------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| BROWSER COMPOSITOR TRANSITION |
| 1. Evaluates user activation & permissions |
| 2. If DENIED ---> Dispatches 'fullscreenerror' (on Element & Document) |
| 3. If GRANTED ---> Reconfigures display & updates document.fullscreenElement |
+-------------------------------------------------------------------------------+
|
v
+-------------------------------------------------------------------------------+
| EVENT BROADCAST: 'fullscreenchange' |
| |
| Target Element (#player) ========> Dispatches 'fullscreenchange' |
| (Bubbles up to Document & Window) |
| |
| document.fullscreenElement === #player (or null if exiting) |
+-------------------------------------------------------------------------------+
In the Fullscreen API, fullscreenchange is that transponder ping. Because users can exit fullscreen at any time using the keyboard (Escape key), trackpad gestures, or OS controls, your UI state must never assume success based solely on a click. You must observe the lifecycle events to keep your play/pause buttons, icons, and analytics in sync.
Technical Deep Dive & Specifications
The fullscreenchange Event
The browser dispatches a fullscreenchange event whenever:
- An element successfully transitions into the Top Layer.
- An element is popped off the Top Layer (transitioning to a previous nested fullscreen element).
- The last element in the stack exits, returning to normal windowed mode.
// Listening at the document level (Recommended)
document.addEventListener('fullscreenchange', (event: Event) => {
if (document.fullscreenElement) {
console.log('Entered fullscreen on:', document.fullscreenElement);
} else {
console.log('Exited fullscreen mode completely.');
}
});
The fullscreenerror Event
If a call to requestFullscreen() fails (or if exitFullscreen() encounters a fatal error), the browser fires the fullscreenerror event:
document.addEventListener('fullscreenerror', (event: Event) => {
console.error('Fullscreen request was rejected by browser policy.', event);
});
Common Causes of fullscreenerror:
- Missing User Activation: Attempting programmatic invocation without an active user gesture.
- Iframe Sandboxing: The element resides inside an
<iframe>missingallow="fullscreen". - Detached DOM Node: The element was removed or detached from the DOM immediately after calling
requestFullscreen(). - Conflicting Window State: The window is minimized or inactive.
Querying document.fullscreenElement
document.fullscreenElement is the single source of truth for fullscreen status:
+-------------------------------------------------------------------------------+
| `document.fullscreenElement` |
+----------------------+--------------------------------------------------------+
| Return Value | State Description |
+----------------------+--------------------------------------------------------+
| `null` | The document is currently in normal windowed mode. |
| | |
| `HTMLElement` | Reference to the specific DOM node currently promoted |
| (e.g. `<video id>`) | into the active Top Layer slot. |
+----------------------+--------------------------------------------------------+
Comparison: Event Listener vs Promise Handling
Modern browsers support both Promises on requestFullscreen() and the fullscreenchange event. How do they compare?
| Feature | requestFullscreen().then() / await |
document.addEventListener('fullscreenchange') |
|---|---|---|
| Scope | Captures only the specific initiating call | Captures ALL transitions (entry, nested, and Escape key exits) |
Handles Keyboard Esc |
โ No (Only catches the entry promise) | โ Yes (Guaranteed to fire on Esc) |
| Error Handling | catch (err) captures the specific TypeError |
Fires global fullscreenerror |
| Best Practice Usage | Immediate async flow control | Global UI synchronization & analytics |
๐ป Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Line 164โ187 (
document.addEventListener('fullscreenchange')): Centralizes all UI updates into a single event handler. When the user exits usingEscape, this handler fires automatically and restores all labels and button icons. - Line 172โ176 (
sessionStartTime): Captures high-precision timestamps (performance.now()) to compute exact user viewing duration metrics. - Line 149โ157 (
triggerErrorBtn): Deliberately executesrequestFullscreen()after asetTimeoutto trigger thefullscreenerrorevent for diagnostic testing. - Line 189โ192 (
document.addEventListener('fullscreenerror')): Catches systemic browser rejections and security failures.
Expected Browser Render Output
- The page renders with a grey badge:
STATE: WINDOWEDanddocument.fullscreenElement: null. - Clicking "Enter Fullscreen" promotes the player and triggers
fullscreenchange. The telemetry log records the exact event and changes the badge to green:STATE: FULLSCREEN ACTIVE. - Pressing
Escapetriggersfullscreenchangeagain, calculating the total viewing session duration (e.g.Session duration: 4.82s) and restoring the icon.
๐๏ธ Hands-On Exercise
๐ฏ The Challenge: Create a Resilient Fullscreen Telemetry & Error Monitor
Instructions:
- Create a media container (
#gameViewport) with an "Enter Fullscreen Game" button. - Build an analytics tracker that logs:
- When fullscreen was entered.
- The screen resolution at the time of entry (
window.innerWidthxwindow.innerHeight). - How many times the user entered and exited during the session.
- Add a warning banner if the user stays in fullscreen for more than 10 seconds.
- Listen for
fullscreenerrorand display a user-friendly modal warning explaining that user gestures are required.
๐ Starter Code Sandbox
โ ๏ธ Common Pitfalls
- Updating UI State Only in Click Handlers: Updating your button label to "Exit" inside
button.addEventListener('click')causes a desynchronization bug when the user exits using theEscapekey. Always update state inside thefullscreenchangelistener. - Attaching
fullscreenchangeOnly to Target Elements on Legacy Browsers: While modern browsers bubblefullscreenchange, some older WebKit engines dispatched it only to theDocument. Listening ondocumentis the most reliable cross-browser approach. - Assuming
event.targetis Always the Fullscreen Element: On exit,document.fullscreenElementisnull. Always verify whetherdocument.fullscreenElementis truthy before reading element properties.
๐ก Pro Tips
- Integrate with Visibility API: Combine
fullscreenchangewithvisibilitychange(document.hidden) to automatically pause full-screen games or video playback if the user switches virtual desktops (e.g. via Alt+Tab or Mission Control). - Dispatch Framework-Level Custom Events: In large web apps, wrap
fullscreenchangein an application-wide event bus or reactive store (e.g., Zustand, Pinia, Redux) to notify decoupled navigation bars and modals.
๐ Key Takeaways
fullscreenchangeis dispatched whenever entering or exiting any layer of fullscreen mode.fullscreenerrorfires when a fullscreen request is denied by permissions or security gating.document.fullscreenElementis the canonical source of truth for the currently promoted DOM node.- Always synchronize UI buttons, icons, and timers inside
fullscreenchangeto account forEscapekey exits. - Listening on
documentensures 100% capture of all transitions across parent and child components. - --