The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Enter fullscreenawait panel.requestFullscreen()View examples
Prefer hidden browser UIawait panel.requestFullscreen({ navigationUI: 'hide' })View examples
Exit fullscreenawait document.exitFullscreen()View examples
Read active elementconst active = document.fullscreenElementView examples
Check availabilityif (document.fullscreenEnabled) enableButton()View examples
Observe state changedocument.addEventListener('fullscreenchange', syncFullscreenUI)View examples
Observe request errorspanel.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 PiPconst pipWindow = await video.requestPictureInPicture()View examples
Exit video PiPawait document.exitPictureInPicture()View examples
Read active PiP videoconst active = document.pictureInPictureElementView examples
Observe PiP entryvideo.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

01

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.

Toggle a viewer with explicit error handling
<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>
Back to quick reference ↑
02

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.

Keep the control label synchronized
<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>
Back to quick reference ↑
03

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.

Delegate presentation features to a trusted player
<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>
Back to quick reference ↑
04

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.

Give a fullscreen viewer complete layout
<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>
Back to quick reference ↑
05

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.

Build a progressively enhanced PiP toggle
<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>
Back to quick reference ↑

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.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. WHATWGFullscreen API Standardfullscreen.spec.whatwg.org
  2. World Wide Web ConsortiumPicture-in-Picturew3.org
  3. World Wide Web ConsortiumPermissions Policyw3.org
  4. WHATWGHTML Standard: The video elementhtml.spec.whatwg.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback