Ce qui se passe
Firefox 151 vient de livrer l'API Document Picture-in-Picture. À ne pas confondre avec le Picture-in-Picture classique, qui détache une vidéo : celle-ci détache un document, donc n'importe quel morceau d'interface. Cours de bourse, conversation, liste de lecture, minuteur, bloc-notes — tout ce qu'on veut garder visible en changeant d'onglet ou d'application.
Chrome la prend en charge, Safari non, et l'API est réservée au bureau.
Comment s'en prendre
Il n'existe pas de détection par requête de fonctionnalité : @supports ne sait
pas interroger une règle @media, la fonction at-rule() n'existe que dans
Chrome et le support des préludes semble abandonné. La détection se fait donc en
JavaScript :
if (!("documentPictureInPicture" in window)) {
document.querySelector("button").remove();
}La création rend une promesse :
const DPIP = await window.documentPictureInPicture.requestWindow({
width: 600,
height: 400,
preferInitialWindowPlacement: true,
});width et height vont par paire — l'une sans l'autre est refusée, et sans les
deux c'est le navigateur qui décide. preferInitialWindowPlacement à true
empêche la mémorisation de la position et de la taille d'une session sur
l'autre ; disallowReturnToOpener masque le bouton « Retour à l'onglet ».
Le contenu, ensuite, se déplace à la main :
const stock = document.querySelector("#stock");
DPIP.document.body.append(stock.cloneNode(true));Le piège
La fenêtre détachée n'hérite d'aucun style. Il faut cloner les <style> et
les <link rel=stylesheet>, de préférence dans un fragment de document pour ne
déclencher qu'un seul recalcul de mise en page :
const styles = document.querySelectorAll("style, [rel=stylesheet]");
const fragment = document.createDocumentFragment();
styles.forEach((el) => fragment.append(el.cloneNode(true)));
DPIP.document.head.append(fragment);Tout cloner n'est d'ailleurs pas nécessaire, et le vrai risque est ailleurs : sorti de son contexte, un composant dont les règles dépendent d'un sélecteur parent perd son apparence. Écrire des sélecteurs qui valent dans les deux fenêtres est la condition d'entrée.
Pour adapter la mise en page au format réduit, la requête média est
@media (display-mode: picture-in-picture). Attention au faux ami : la
pseudo-classe :picture-in-picture, elle, appartient à l'API vidéo.
Deux comportements à connaître pour finir. Une nouvelle fenêtre remplace la
précédente, et le focus bascule systématiquement vers elle — faire du bouton
d'ouverture une bascule demande alors deux clics, ce qui n'en vaut pas la peine
puisque la fenêtre porte déjà sa croix de fermeture. Et l'événement enter se
déclenche à l'ouverture, sans usage évident.
Source : CSS-Tricks, Creating Web Widgets Using the Document Picture-in-Picture API