Creating Web Widgets Using the Picture-in-Picture API for Documents | CSS Tricks


Firefox 151 recently released the Picture-in-Picture API. This is not the same as the (regular?) Picture-in-Picture API, which puts the video in a resizable window that remains visible even after switching browser tabs or OS windows. No, Document The Picture in Picture API allows us to post anything out the window.

I guess we can think of these windows as web widgets. We can use them for floating stock quotes, chat conversations, playlists, to-do lists, notes, spreadsheets – anything we would like to keep on the screen at all times.

The general idea is that we create a Picture-in-Picture window of the document (DPIP window) and then put HTML, CSS and JavaScript into it. It’s pretty simple when you think about it, but as we explore how the Document Picture-in-Picture API works, we’re going to look at a slightly more complex scenario that you’re likely to encounter.

We are going to clone the stock ticker from the main document. V DPIP window. Not only does this give us a chance to talk about some of the relevant media queries and pseudo-classes that we’ll use to write the target CSS for the DPIP window, but it’s also a stark reminder that taking an HTML component out of context can break the CSS, so you’ll need to keep that in mind.

This is the listed stock ticker:

But for this to work, you need to open the demo in debug mode. This is because picture-in-picture doesn’t work in nested browsing contexts like CodePen. With.

Also, Safari doesn’t yet support the DPIP API, so make sure you’re using Chrome or Firefox.

Ready to get started?

JavaScript of it all

First we need to check if the browser supports the Picture in Picture API. I think it would be nice to have this feature, so why wait for Safari support? Unfortunately, there is no way to know whether @media (display-mode: picture-in-picture) supported using function queries (@supports), because at-rule() the feature is only supported by Chrome, and any plans to support foreplay (that’s this part: (display-mode: picture-in-picture)), seems to have been discarded anyway.

It would be great to do this:

@supports at-rule(@media; display-mode: picture-in-picture) {
  /* DPIP supported */
}

Note: The Safari Technology Preview 251 release notes mention support for rule discovery in @supports but it is unclear when this will be implemented. And Firefox 155 announced its support just a day after publication.

Instead, we need to test browser support using JavaScript, removing the button if DPIP is not supported, or creating a DPIP window if it is supported):

if (!("documentPictureInPicture" in window)) {
  /* DPIP not supported (remove button) */
  document.querySelector("button").remove();
} else {
  /* DPIP supported (listen for button click) */
  document.querySelector("button").addEventListener("click", async () => {
    /* ... */
  });
}

Keep in mind that the Document Picture-in-Picture API is desktop-only, so the test above takes that into account while also illustrating why it’s fully functional at-rule() a feature would be very useful.

In terms of creating a DPIP window, one thing we might want to do first is process the existing DPIP window. DPIP windows replace the existing DPIP windows, so we don’t have to worry about that part, but we do need to decide what will happen if the button is clicked a second time. The code below closes the DPIP window if it is already open, effectively turning the button into a radio button:

document.querySelector("button").addEventListener("click", async () => {
  /* If the DPIP window is open, close it */
  if (window.documentPictureInPicture.window) {
    window.documentPictureInPicture.window.close();
  }
});

The problem is that the focus always switches to the DPIP window, so it may take two button clicks to turn off the DPIP window. One solution to this problem is to clone the button into the DPIP window, but the DPIP window already has a Close icon button, so there’s no point in doing that. Personally, I wouldn’t do anything by allowing subsequent button presses to recreate the DPIP window. In fact, if the user moves or resizes the DPIP window, subsequent button presses will return it to its original position and size (with the correct settings).

In this regard, let’s talk about creating DPIP windows and the options mentioned. It is quite obvious that width And height there are options, but keep in mind that we cannot install one without the other, and if we install neither, the browser will make the choice. preferInitialWindowPlacement option if set to truePrevents the browser from storing the position and size of the DPIP window. disallowReturnToOpener option (not used here) if set to truehides the “Return to Tab” icon button (which does the same thing as the “Close” icon button, but also returns the user to the original tab).

/* Create the DPIP window */
const DPIP = await window.documentPictureInPicture.requestWindow({
  width: 600,
  height: 400,
  preferInitialWindowPlacement: true
});

requestWindow() method DocumentPictureInPicture interface returns a promise (that’s why we use async And await), which means that we can take care of everything else while the window is being prepared.

We can clone HTML into a DPIP window like this:

/* Select the component */
const stock = document.querySelector("#stock");

/* Clone the component and append it to the DPIP  */
DPIP.document.body.append(stock.cloneNode(true));

But to clone multiple elements, we will have to use a different approach. This is what we are going to do by cloning everything s and sand

Leave a Reply

Your email address will not be published. Required fields are marked *