### Access and Create Document Picture-in-Picture Window
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Check if a PiP window is currently open using `documentPictureInPicture.window`. If no window exists, `requestWindow()` can be called to create a new one. This snippet demonstrates how to get an existing window or create a new one and then populate its body.
```javascript
// Check if a PiP window is currently open
function isPiPWindowOpen() {
return documentPictureInPicture.window !== null;
}
// Get the current PiP window or open a new one
async function getOrCreatePiPWindow() {
// Check if there's already an open PiP window
const existingWindow = documentPictureInPicture.window;
if (existingWindow) {
console.log('Using existing PiP window');
return existingWindow;
}
// No existing window, create a new one
console.log('Creating new PiP window');
return await documentPictureInPicture.requestWindow({
width: 320,
height: 240
});
}
// Example usage
document.querySelector('#pip-button').addEventListener('click', async () => {
const pipWindow = await getOrCreatePiPWindow();
pipWindow.document.body.innerHTML = '
Hello PiP!
';
});
```
--------------------------------
### Configure and Open Document Picture-in-Picture Windows
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Demonstrates various configurations for the requestWindow method, including setting dimensions, controlling the return-to-opener UI, and managing window placement.
```javascript
// All available options for requestWindow()
async function openConfiguredPiPWindow() {
const options = {
// Initial width of the PiP window viewport (in pixels)
// Must be specified together with height, or both must be 0/omitted
width: 400,
// Initial height of the PiP window viewport (in pixels)
height: 300,
// When true, hints to the UA to hide the "return to tab" button
// Useful for experiences where returning doesn't make sense
disallowReturnToOpener: false,
// When true, hints to the UA to use default position/size
// instead of remembering previous PiP window position
preferInitialWindowPlacement: false
};
const pipWindow = await documentPictureInPicture.requestWindow(options);
// Window inherits the opener's document mode (quirks/standards)
console.log('Document mode:', pipWindow.document.compatMode);
return pipWindow;
}
// Open with return-to-opener disabled (e.g., for standalone widgets)
async function openStandaloneTimer() {
const pipWindow = await documentPictureInPicture.requestWindow({
width: 200,
height: 100,
disallowReturnToOpener: true // Hide "back to tab" UI
});
pipWindow.document.body.innerHTML = `
25:00
`;
return pipWindow;
}
// Open with fresh positioning (ignore previous position)
async function openFreshPiPWindow() {
const pipWindow = await documentPictureInPicture.requestWindow({
width: 320,
height: 240,
preferInitialWindowPlacement: true // Don't remember last position
});
return pipWindow;
}
```
--------------------------------
### Implement Video Conferencing in PiP
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Creates a custom PiP window containing a video grid for remote streams, a local video preview, and interactive controls for muting and camera toggling. Requires an active local stream and a list of remote streams.
```javascript
// Multi-stream video conferencing in PiP
async function enterVideoConferencePiP(localStream, remoteStreams) {
const pipWindow = await documentPictureInPicture.requestWindow({
width: 480,
height: 360
});
// Add styles
const style = pipWindow.document.createElement('style');
style.textContent = `
body { margin: 0; background: #1a1a1a; }
.video-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 4px;
height: calc(100% - 50px);
}
video { width: 100%; height: 100%; object-fit: cover; }
.local-video {
position: absolute; bottom: 60px; right: 10px;
width: 120px; border: 2px solid #fff; border-radius: 8px;
}
.controls {
position: fixed; bottom: 0; width: 100%;
display: flex; justify-content: center; gap: 10px;
padding: 10px; background: #2a2a2a;
}
.controls button {
padding: 10px 20px; border-radius: 20px;
border: none; cursor: pointer;
}
.mute-btn.muted { background: #ff4444; color: white; }
`;
pipWindow.document.head.appendChild(style);
// Create video grid for remote participants
const videoGrid = pipWindow.document.createElement('div');
videoGrid.className = 'video-grid';
remoteStreams.forEach((stream, index) => {
const video = pipWindow.document.createElement('video');
video.srcObject = stream;
video.autoplay = true;
video.playsInline = true;
videoGrid.appendChild(video);
});
pipWindow.document.body.appendChild(videoGrid);
// Local video preview (picture-in-picture within PiP)
const localVideo = pipWindow.document.createElement('video');
localVideo.className = 'local-video';
localVideo.srcObject = localStream;
localVideo.autoplay = true;
localVideo.muted = true; // Prevent feedback
localVideo.playsInline = true;
pipWindow.document.body.appendChild(localVideo);
// Control bar
const controls = pipWindow.document.createElement('div');
controls.className = 'controls';
// Mute button
const muteBtn = pipWindow.document.createElement('button');
muteBtn.className = 'mute-btn';
muteBtn.textContent = 'π€ Mute';
muteBtn.addEventListener('click', () => {
const audioTrack = localStream.getAudioTracks()[0];
audioTrack.enabled = !audioTrack.enabled;
muteBtn.classList.toggle('muted', !audioTrack.enabled);
muteBtn.textContent = audioTrack.enabled ? 'π€ Mute' : 'π Unmute';
});
// Camera toggle
const cameraBtn = pipWindow.document.createElement('button');
cameraBtn.textContent = 'πΉ Camera';
cameraBtn.addEventListener('click', () => {
const videoTrack = localStream.getVideoTracks()[0];
videoTrack.enabled = !videoTrack.enabled;
cameraBtn.textContent = videoTrack.enabled ? 'πΉ Camera' : 'π· Camera Off';
});
// Leave call
const leaveBtn = pipWindow.document.createElement('button');
leaveBtn.textContent = 'π Leave';
leaveBtn.style.background = '#ff4444';
leaveBtn.style.color = 'white';
leaveBtn.addEventListener('click', () => {
window.focus();
pipWindow.close();
// Trigger leave call logic in main app
window.dispatchEvent(new CustomEvent('leave-call'));
});
controls.append(muteBtn, cameraBtn, leaveBtn);
pipWindow.document.body.appendChild(controls);
return pipWindow;
}
```
--------------------------------
### Move elements to and from PiP window
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Demonstrates moving a video player element into a PiP window and returning it to the main document upon window closure.
```javascript
// Complete example: Custom video player with PiP
let pipWindow = null;
async function enterVideoPlayerPiP() {
const player = document.querySelector('#player');
const playerContainer = document.querySelector('#player-container');
// Open PiP window sized to the player
pipWindow = await documentPictureInPicture.requestWindow({
width: player.clientWidth,
height: player.clientHeight
});
// Copy styles to PiP window
const style = pipWindow.document.createElement('style');
style.textContent = `
body { margin: 0; overflow: hidden; }
#player { width: 100%; height: 100%; }
video { width: 100%; height: 100%; object-fit: contain; }
.controls {
position: absolute; bottom: 0; width: 100%;
background: linear-gradient(transparent, rgba(0,0,0,0.7));
padding: 10px; box-sizing: border-box;
}
`;
pipWindow.document.head.appendChild(style);
// Move the player element to PiP window
pipWindow.document.body.appendChild(player);
// Add visual indicator in main page
playerContainer.classList.add('pip-mode');
playerContainer.innerHTML = '
Player is in Picture-in-Picture mode
';
// Handle PiP window closing
pipWindow.addEventListener('pagehide', () => {
// Move player back to main document
const pipPlayer = pipWindow.document.querySelector('#player');
playerContainer.classList.remove('pip-mode');
playerContainer.innerHTML = '';
playerContainer.appendChild(pipPlayer);
pipWindow = null;
}, { once: true });
}
// HTML structure expected:
//
//
//
//
...
//
//
```
--------------------------------
### Open a Document Picture-in-Picture Window
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Use `documentPictureInPicture.requestWindow()` to open a new PiP window with specified dimensions. This method requires a user gesture and returns a Promise resolving to the new Window object. It handles copying stylesheets and includes error handling for unsupported features or disallowed operations.
```javascript
async function openPictureInPicture() {
try {
// Request a PiP window with specified width and height
const pipWindow = await documentPictureInPicture.requestWindow({
width: 400,
height: 300,
disallowReturnToOpener: false,
preferInitialWindowPlacement: false
});
// The pipWindow is a regular Window object with an empty document
console.log('PiP window opened:', pipWindow);
console.log('Document URL:', pipWindow.document.URL); // "about:blank"
// Copy stylesheets from the main document to the PiP window
[...document.styleSheets].forEach((styleSheet) => {
try {
const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
const style = document.createElement('style');
style.textContent = cssRules;
pipWindow.document.head.appendChild(style);
} catch (e) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = styleSheet.href;
pipWindow.document.head.appendChild(link);
}
});
return pipWindow;
} catch (error) {
// Handle errors: NotSupportedError, NotAllowedError, or RangeError
if (error.name === 'NotSupportedError') {
console.error('Document PiP is not supported');
} else if (error.name === 'NotAllowedError') {
console.error('Requires user gesture or top-level context');
} else if (error.name === 'RangeError') {
console.error('Width and height must both be specified or both be zero');
}
throw error;
}
}
```
--------------------------------
### documentPictureInPicture.requestWindow()
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Opens a new picture-in-picture window with a blank document. Requires a user gesture to execute.
```APIDOC
## documentPictureInPicture.requestWindow()
### Description
Opens a new picture-in-picture window with a blank document that can be populated with arbitrary HTML elements. This method requires a user gesture to call.
### Parameters
#### Request Body
- **width** (number) - Optional - The width of the PiP window.
- **height** (number) - Optional - The height of the PiP window.
- **disallowReturnToOpener** (boolean) - Optional - Whether to prevent the return-to-opener button.
- **preferInitialWindowPlacement** (boolean) - Optional - Whether to prefer initial window placement.
### Response
#### Success Response (Promise)
- **Window** (object) - Resolves to the new Window object representing the PiP window.
### Errors
- **NotSupportedError** - Document PiP is not supported.
- **NotAllowedError** - Requires user gesture or top-level context.
- **RangeError** - Width and height must both be specified or both be zero.
```
--------------------------------
### Enter Picture-in-Picture Mode
Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md
Initiates Picture-in-Picture mode for a video player. Requires the video element to be present in the DOM. Styles the container to indicate PiP mode and appends the player to the PiP window.
```javascript
let pipWindow = null;
async function enterPiP() {
const player = document.querySelector("#player");
const pipOptions = {
width: player.clientWidth,
height: player.clientHeight,
};
pipWindow = await documentPictureInPicture.requestWindow(pipOptions);
// Style remaining container to imply the player is in PiP.
const playerContainer = document.querySelector("#player-container");
playerContainer.classList.add("pip-mode");
// Add player to the PiP window.
pipWindow.document.body.append(player);
// Listen for the PiP closing event to put the video back.
pipWindow.addEventListener("pagehide", onLeavePiP.bind(pipWindow), {
once: true,
});
}
```
--------------------------------
### Handle PiP window creation events
Source: https://context7.com/wicg/document-picture-in-picture/llms.txt
Use the 'enter' event or 'onenter' property to detect when a PiP window is opened and access the window object.
```javascript
// Listen for PiP window creation
documentPictureInPicture.addEventListener('enter', (event) => {
const pipWindow = event.window;
console.log('PiP window opened with dimensions:',
pipWindow.innerWidth, 'x', pipWindow.innerHeight);
// Set up the PiP window content
pipWindow.document.body.style.margin = '0';
pipWindow.document.body.style.backgroundColor = '#1a1a1a';
pipWindow.document.body.style.color = 'white';
// Add a close button to the PiP window
const closeButton = pipWindow.document.createElement('button');
closeButton.textContent = 'Close PiP';
closeButton.style.cssText = 'position: fixed; top: 10px; right: 10px;';
closeButton.addEventListener('click', () => pipWindow.close());
pipWindow.document.body.appendChild(closeButton);
});
// Alternative: using onenter property
documentPictureInPicture.onenter = (event) => {
console.log('PiP window ready:', event.window);
};
```
--------------------------------
### Define HTML structure for PiP
Source: https://github.com/wicg/document-picture-in-picture/blob/main/README.md
Provides the basic markup for a video player container and a trigger button to initiate the Picture-in-Picture experience.
```html