The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Enter fullscreen | await panel.requestFullscreen() | View examples |
| Prefer hidden browser UI | await panel.requestFullscreen({ navigationUI: 'hide' }) | View examples |
| Exit fullscreen | await document.exitFullscreen() | View examples |
| Read active element | const active = document.fullscreenElement | View examples |
| Check availability | if (document.fullscreenEnabled) enableButton() | View examples |
| Observe state change | document.addEventListener('fullscreenchange', syncFullscreenUI) | View examples |
| Observe request errors | panel.addEventListener('fullscreenerror', reportFailure) | View examples |
| Style the fullscreen element | .viewer:fullscreen { inline-size: 100%; block-size: 100%; } | View examples |
| Style fullscreen backdrop | .viewer::backdrop { background: #000; } | View examples |
| Delegate both features | <iframe
src="/player"
allow="fullscreen; picture-in-picture">
</iframe> | View examples |
| Enter video PiP | const pipWindow = await video.requestPictureInPicture() | View examples |
| Exit video PiP | await document.exitPictureInPicture() | View examples |
| Read active PiP video | const active = document.pictureInPictureElement | View examples |
| Observe PiP entry | video.addEventListener('enterpictureinpicture', syncPipUI) | View examples |
| Discourage video PiP | <video controls disablepictureinpicture></video> | View examples |
Fullscreen places an element in the browser's top layer; Picture-in-Picture places a playing video in a floating window. Both modes are asynchronous, policy-controlled, user-reversible presentation states—not layout assumptions. Request them directly from trusted user activation, catch rejected promises, derive interface state from events and the document's current element, and always preserve native controls and an obvious way back.
Step by step
Detailed examples
Request and exit fullscreen asynchronously
requestFullscreen operates on an element and returns a promise. navigationUI is a preference rather than a guarantee. exitFullscreen operates on Document; call it only while this document has a fullscreen element. The browser or user can end fullscreen independently, so a resolved entry promise does not mean the state lasts until application code exits it.
<section id="viewer"><h2>Report preview</h2><button id="toggle" type="button">Enter fullscreen</button></section>
<script>
toggle.addEventListener('click', async () => {
try {
if (document.fullscreenElement) await document.exitFullscreen();
else await viewer.requestFullscreen({ navigationUI: 'hide' });
} catch (error) {
console.error(`Fullscreen failed: ${error.name}`);
}
});
</script> Render controls from authoritative state events
document.fullscreenElement is the current state for the document tree, and fullscreenEnabled combines implementation support with policy availability. fullscreenchange fires after transitions and is also needed when the user exits with browser controls or Escape. Do not maintain an independent isFullscreen flag that can drift from browser state.
<div id="stage"><button id="full" type="button">Enter fullscreen</button></div>
<script>
full.disabled = !document.fullscreenEnabled;
full.addEventListener('click', async () => {
try {
if (document.fullscreenElement) await document.exitFullscreen();
else await stage.requestFullscreen();
} catch (error) { console.error(error); }
});
document.addEventListener('fullscreenchange', () => {
full.textContent = document.fullscreenElement ? 'Exit fullscreen' : 'Enter fullscreen';
});
</script> Keep requests inside activation and permissions boundaries
Fullscreen entry and script-initiated Picture-in-Picture generally require transient user activation, so call the request directly from the click handler rather than after unrelated asynchronous work. Requests can reject because the element is detached, the document is inactive, policy disallows the feature, or another precondition fails. Embedded documents also depend on the iframe allow policy and any response-header Permissions-Policy.
<iframe
src="https://player.example/video/42"
title="Training video"
allow="fullscreen; picture-in-picture"
></iframe>
<script>
const panel = document.querySelector('iframe');
panel.addEventListener('fullscreenerror', () => {
console.warn('Fullscreen was unavailable or denied.');
});
</script> Style the top-layer presentation without hiding escape routes
The :fullscreen pseudo-class matches the fullscreen element and its ancestor shadow hosts; ::backdrop styles the backdrop generated behind it. Supply complete sizing, contrast, and overflow rules because the fullscreen viewport differs from an embedded card. Keep an accessible exit control and do not imitate browser chrome or obscure the browser's own security messaging.
<section class="viewer"><button type="button" onclick="document.exitFullscreen()">Exit fullscreen</button><p>Presentation content</p></section>
<style>
.viewer:fullscreen {
box-sizing: border-box;
display: grid;
place-content: center;
inline-size: 100%;
block-size: 100%;
padding: 2rem;
color: white;
background: #111827;
}
.viewer::backdrop { background: #000; }
</style> Treat Picture-in-Picture as a video-owned mode
The standardized Picture-in-Picture API applies to HTMLVideoElement and is feature- and policy-dependent. Feature-detect requestPictureInPicture and document.pictureInPictureEnabled, request it from user activation while the video is in a usable state, and track enterpictureinpicture and leavepictureinpicture. disablepictureinpicture is a user-agent hint, not an access-control or content-protection mechanism.
<video id="video" controls src="lesson.mp4"></video>
<button id="pip" type="button">Open Picture-in-Picture</button>
<script>
const supported = 'pictureInPictureEnabled' in document && 'requestPictureInPicture' in HTMLVideoElement.prototype;
pip.hidden = !supported || !document.pictureInPictureEnabled;
pip.addEventListener('click', async () => {
try {
if (document.pictureInPictureElement) await document.exitPictureInPicture();
else await video.requestPictureInPicture();
} catch (error) { console.error(`PiP failed: ${error.name}`); }
});
video.addEventListener('enterpictureinpicture', () => { pip.textContent = 'Close Picture-in-Picture'; });
video.addEventListener('leavepictureinpicture', () => { pip.textContent = 'Open Picture-in-Picture'; });
</script> Local code tester
Toggle a panel in fullscreen
Use the button to request fullscreen and watch the label follow browser-controlled state changes. Embedded preview policy may deny the request.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



