Start · Sprachen · JavaScript · Referenz · DocumentPictureInPicture

DocumentPictureInPicture

Schnittstelle

Einstiegspunkt der Document Picture-in-Picture API: öffnet über <code>window.documentPictureInPicture</code> ein immer oben liegendes Fenster mit beliebigem HTML-Inhalt und verwaltet es.

seit JavaScript Chrome/Edge 116+ (experimentell, nicht Baseline) Kategorie: webapi

Signatur

interface DocumentPictureInPicture extends EventTarget

Beschreibung

Das Interface DocumentPictureInPicture gehört zur Document Picture-in-Picture API. Die Instanz erreicht man über window.documentPictureInPicture. Anders als das klassische Bild-im-Bild für <video> (HTMLVideoElement.requestPictureInPicture()) kann man damit ein komplettes Dokument in einem schwebenden Fenster anzeigen, das immer im Vordergrund bleibt. Darin können beliebige DOM-Elemente stehen, etwa ein Videoplayer mit eigenen Bedienelementen, eine Videokonferenz-Kachel, ein Timer oder ein Notizblock.

Das Interface bietet:

  • requestWindow(options): öffnet das PiP-Fenster und gibt ein Promise<Window> zurück. Das Window-Objekt ist anfangs leer (about:blank, gleicher Ursprung wie der Öffner).
  • window (nur lesbar): das aktuell geöffnete PiP-Window oder null.
  • Event enter: wird ausgelöst, sobald das PiP-Fenster geöffnet ist (DocumentPictureInPictureEvent mit Eigenschaft window). Registrieren lässt es sich über addEventListener("enter", …) oder onenter.

Üblicherweise verschiebt man vorhandene DOM-Knoten mit append() ins PiP-Fenster. Sie behalten dabei ihren Zustand, ein laufendes Video spielt also weiter. Wird das Fenster geschlossen, holt man sie im Event pagehide des PiP-Fensters wieder zurück. Stylesheets werden nicht automatisch übernommen, die muss man selbst ins neue Dokument kopieren.

Parameter

Name Typ Default Beschreibung
$options.width number vom Browser gewählt Gewünschte Anfangsbreite des PiP-Fensters in CSS-Pixeln. Der Browser kann den Wert begrenzen. Wenn angegeben, muss auch height gesetzt werden.
$options.height number vom Browser gewählt Gewünschte Anfangshöhe in CSS-Pixeln. Wenn angegeben, muss auch width gesetzt werden.
$options.disallowReturnToOpener boolean false Bei true blendet der Browser die Schaltfläche „Zurück zum Tab“ im PiP-Fenster aus.
$options.preferInitialWindowPlacement boolean false Bei true öffnet sich das Fenster mit Standardgröße und -position, statt sich an eine frühere Größe oder Position zu erinnern (Chrome 130+).

Beispiele

Videoplayer ins PiP-Fenster verschieben und zurückholen

const pipButton = document.querySelector('#pip-button');
const player = document.querySelector('#player');
const playerContainer = document.querySelector('#player-container');

pipButton.addEventListener('click', async () => {
  if (!('documentPictureInPicture' in window)) {
    console.warn('Document Picture-in-Picture wird nicht unterstützt.');
    return;
  }

  // Muss direkt in einem Klick-Handler laufen (Nutzeraktivierung)
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: 480,
    height: 270,
  });

  // Element verschieben – das Video läuft weiter
  pipWindow.document.body.append(player);

  // Beim Schließen wieder ins Hauptdokument zurückholen
  pipWindow.addEventListener('pagehide', () => {
    playerContainer.append(player);
  });
});

Stylesheets ins PiP-Dokument kopieren

const copyStyles = (pipWindow) => {
  for (const sheet of document.styleSheets) {
    try {
      // Gleicher Ursprung: Regeln direkt übernehmen
      const css = [...sheet.cssRules].map((rule) => rule.cssText).join('\n');
      const style = pipWindow.document.createElement('style');
      style.textContent = css;
      pipWindow.document.head.append(style);
    } catch {
      // Fremder Ursprung: per <link> einbinden
      const link = pipWindow.document.createElement('link');
      link.rel = 'stylesheet';
      link.href = sheet.href;
      pipWindow.document.head.append(link);
    }
  }
};

document.querySelector('#open-timer').addEventListener('click', async () => {
  const pipWindow = await documentPictureInPicture.requestWindow();
  copyStyles(pipWindow);
  pipWindow.document.body.innerHTML = '<p class="timer">00:00</p>';
});

Auf das enter-Event reagieren und den Zustand prüfen

documentPictureInPicture.addEventListener('enter', (event) => {
  const pipWindow = event.window;
  console.log('PiP geöffnet:', pipWindow.innerWidth, 'x', pipWindow.innerHeight);
});

// Alternativ über die on-Property:
// documentPictureInPicture.onenter = (event) => { ... };

const closePip = () => {
  // window ist null, wenn kein PiP-Fenster offen ist
  documentPictureInPicture.window?.close();
};

document.querySelector('#close-pip').addEventListener('click', closePip);
PiP geöffnet: 480 x 270

// Wichtig · Fallstricke

  • Browser-Unterstützung: Bisher gibt es die API nur in Chromium-Browsern auf dem Desktop (Chrome/Edge 116+). Firefox, Safari und mobile Browser unterstützen sie nicht, daher immer per 'documentPictureInPicture' in window prüfen und eine Alternative anbieten.
  • Sicherer Kontext: Die API funktioniert nur über HTTPS bzw. localhost.
  • Nutzeraktivierung erforderlich: requestWindow() klappt nur direkt nach einer Nutzeraktion wie einem Klick. Sonst gibt es einen NotAllowedError.
  • Pro Tab kann nur ein PiP-Fenster offen sein. Ein weiterer Aufruf schließt das bestehende Fenster. Aus einem iframe oder aus dem PiP-Fenster selbst lässt sich kein PiP-Fenster öffnen.
  • Das PiP-Fenster lässt sich nicht per Skript an eine andere Stelle verschieben oder auf andere Adressen navigieren. resizeTo()/resizeBy() funktionieren nur nach einer Nutzeraktion im PiP-Fenster.
  • CSS wird nicht vererbt, Stylesheets müssen kopiert werden. Event-Listener an verschobenen Elementen bleiben erhalten. Code, der mit dem globalen document arbeitet, bezieht sich aber weiterhin auf das Hauptdokument, verwende dort element.ownerDocument.
  • Mit der Media-Query @media all and (display-mode: picture-in-picture) lassen sich Stile speziell für das PiP-Fenster schreiben.