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: Fallback option for embedding CodePen 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 s if you need them, or any resources that the DPIP window requires). However it is quite simple – use querySelectorAll() create an array of NodeList objects and createDocumentFragment() to create an arbitrary DOM tree before iterating over the array using forEach() and cloning each node into the specified document fragment behind the scenes. Finally, add the entire document fragment to the file of the DPIP window, causing just one reflow instead of multiple, which is more performant. And remember, cloning everything probably isn’t necessary, so adjust as needed. /* Select all Here’s the full JavaScript snippet from the demo, which you’ll probably want to extend (at least to add error handling): 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 () => { /* Create the DPIP window */ const DPIP = await window.documentPictureInPicture.requestWindow({ width: 600, height: 400, preferInitialWindowPlacement: true }); /* Select the component */ const stock = document.querySelector("#stock"); /* Clone the component and append it to the DPIP */ DPIP.document.body.append(stock.cloneNode(true)); /* Select all CSS processing Remember: if you are taking HTML out of context (along with its CSS) and putting it into a DPIP window, make sure the CSS selectors are not too specific and are written for both contexts. That being said, you may want to write some targeted CSS specifically for each window, and this is where display-mode it includes a media query. It’s pretty self-explanatory – here’s what I use in the demo to set up the container: #stock { width: fit-content; border-radius: 0.7rem; @media (display-mode: picture-in-picture) { width: 100%; height: 100%; border-top-left-radius: 0; border-top-right-radius: 0; } } Also note that :picture-in-picture pseudo class is for ordinary Picture-in-Picture API No Picture-in-Picture Document API. Summing up I couldn’t think of a good use for the vaguely named enter event that fires when a DPIP window is opened (not to be confused with enterpictureinpicture event for a regular picture-in-picture): documentPictureInPicture.addEventListener("enter", (event) => { /* DPIP window opened */ }); Otherwise, I think this is the end of the Document Picture-in-Picture API. It’s not a big or complex API, but it looks like it could be really useful? Post navigation Two Alleged ‘TeamPCP’ Hackers Arrested in Australia – Krebs on Security OpenClaw went viral. Get to know the maintenance building and ensure its security.