JavaScript Picture-in-Picture Table
| Piece | What it does | Field note |
|---|---|---|
video.requestPictureInPicture() | Float the video | User gesture required - like share and clipboard |
document.pictureInPictureElement | WHO is floating | The element itself - check before toggling |
enterpictureinpicture / leave | The events | User can enter/exit from the OS UI - not just your button |
disablePictureInPicture | Opt a video out | Attribute - picture-in-picture hidden from its menu |
resize event on the PiP window | Know the float size | windowWidth/windowHeight - responsive INSIDE the float |
Auto-PiP (mediastream) | Non-video floats | autoplay policy applies - canvas/mediastream PiP is the newer sibling |
Document PiP (Chrome) | Float ANY element | documentPictureInPicture - arbitrary DOM, not just video |
Battery courtesy | Exit on tab close | PiP outlives the tab - close handlers matter |
Picture-in-Picture floats the VIDEO above every window: video.requestPictureInPicture() detaches the element into an OS-level mini window that survives tab switches and minimization - the watch-while-working feature, gated on a user gesture like share and clipboard.
Bottom line: the video keeps playing from the SAME element - playback state, handlers and MediaSession all persist across the float. The events cut both ways: users can enter and exit PiP from the OS UI, so listen rather than assume, and the resize event reports the float's size for responsive controls inside.
The honest part: classic PiP floats a VIDEO element only. Document PiP (Chrome) extends the float to arbitrary DOM - a whole widget with buttons and lists - which turns it from a video feature into a general mini-window API on supporting browsers.
How to use
- Toggle on click: btn.onclick = () => document.pictureInPictureElement ? document.exitPictureInPicture() : video.requestPictureInPicture() - the standard toggle with identity check.
- Stay responsive in the float: video.addEventListener('resize', ...) on the VIDEO element fires when the PiP window resizes - restyle controls for the small frame.
- Handle OS-driven exits: 'leavepictureinpicture' fires when the user closes the float from the OS UI - update your button state, it was not your click.
Frequently asked questions
What stays attached to the video when it floats?
Everything, because it is the SAME element. The PiP window renders the actual <video> element - play/pause events, MediaSession handlers, playbackRate, caption tracks and your JS all keep working unchanged; the element just renders into the float window instead of the page. Two consequences: playback state persists (no re-buffer, no seek reset), and controls INSIDE the float must be built by listening to the video's own events - the float window shows only the video surface, with OS-provided minimal controls plus anything you draw via canvas overlays. The page element becomes a placeholder frame while the element is floating.
Why do the both-directions events matter more than the request call?
Because users control the float too. enterpictureinpicture and leavepictureinpicture fire whether the transition came from YOUR button or the OS UI (the video's context menu, media controls, the float's close button). UI code that only tracks its own button state desyncs the moment the user closes the float themselves - the in-page placeholder needs to switch back to the embedded view. The standard pattern: a single sync function reading document.pictureInPictureElement, called from both events and your own toggle - state follows reality, whoever initiates the change.
When is Document PiP worth the extra support surface?
When the floating experience is more than video. Classic PiP shows one video surface with OS controls; Document PiP (Chrome's documentPictureInPicture) opens a real mini-window containing ARBITRARY DOM - playlist buttons, chat, quiz controls, workout step lists next to the video. That is a mini-app window: same-origin styles can be copied in, and the window can be resized by the user. The cost: Chromium-only, so it is an enhancement layered on classic PiP and in-page playback (feature-detect documentPictureInPicture). For watch-parties, fitness apps and tutorials with interactive steps, the DOM float is the product; for plain video, classic PiP is enough.
What are the battery and lifecycle considerations?
PiP outlives the page's visibility, which is both the feature and the obligation. The float keeps playing while the user works in another app - if your player buffers aggressively or runs heavy JavaScript on timeupdate, those costs continue in the float (and MediaSession-driven playback can outlive the tab entirely via the OS controls). The hygiene: respect disablePictureInPicture on videos that should never float (ads, background loops), release resources on leavepictureinpicture if the float was the only reason for playback, and remember the float persists past tab CLOSE until the video ends - close handlers and MediaSession stop handlers are the cleanup that matters.