veilletech.fr
28 août Feed du jour
#09 WEB Article

Détacher un widget de l'onglet avec le PiP

Une fenêtre flottante, oui — mais elle arrive sans vos styles.

L'API Document Picture-in-Picture détache un document HTML entier dans une fenêtre flottante, pas seulement une vidéo. Utile pour un minuteur ou un tableau de bord — à condition de recopier soi-même les styles, car la fenêtre n'hérite de rien.

2 min de lecturevidéo 1:18
Partager
Sommaire4 sections
  1. Ce qui se passe
  2. Comment s'en prendre
  3. Le piège
  4. À retenir

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 :

JavaScript
if (!("documentPictureInPicture" in window)) {
  document.querySelector("button").remove();
}

La création rend une promesse :

JavaScript
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 :

JavaScript
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 :

JavaScript
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