Recreating Spotify’s Floating pop-ups
Recently I had come in contact with the Picture In Picture API that is also used on Spotify to pop out the currently playing song into a separate window.
If you have no idea what I’m talking about, there’s a little button inside Spotify’s web app that resembles a screen. If you click on this button, a window will pop up that is completely separate from the browser. It can overlay other tabs and work independently from the tab it is launched from, so users can keep streaming content while browsing other sites or using other applications.
What is The Picture in Picture API?
Picture-in-Picture is a new API that allows users to play and view video
elements in a completely separate overlay from the main browser. Its main purpose is to enable users to consume content from one website while they are surfing the web or using other applications.
It’s been with Android since the spring of 2017 but is coming to the web and you can already use it in Chrome and partially supported in Firefox and Safari. For the full list of browser support, you can refer to the table on caniuse.
How Can I Use It?
For the tutorial, I’ve created this small snippet:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Picture in Picture</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="app">
<video class="video" src="video.mp4" controls></video>
<div class="controls">
<img class="toggle-pic-in-pic" src="picinpic.svg" alt="" />
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Nothing fancy going on, we have a video tag and a control which will trigger the picture in picture API.
After applying some styles, this is how it looks in the browser:
You will notice that if you click on the three dots in the controls of the video, you already have an option to open it in picture in picture. To outsource this behavior and make other buttons on the page act as a pop-up button, add the following event listener to the icon:
const video = document.querySelector('.video');
const pipButton = document.querySelector('.toggle-pic-in-pic');
pipButton.addEventListener('click', event => {
if (video !== document.pictureInPictureElement) {
video.requestPictureInPicture();
} else {
document.exitPictureInPicture();
}
});
It’s important to note that only video
elements can be requested to enter into PiP mode. But first, we need to test if an element is already in PiP mode. document.pictureInPictureElement
will return the element that is currently in PiP mode, otherwise it will return null
. Before requesting new elements to be promoted into PiP mode, you need to close already running ones.
We can also listen for multiple events, such as entering, leaving, or resizing the window:
let pipWindow = null;
let resizeEvent = () => console.log(`window resized to ${pipWindow.width}x${pipWindow.height}`);
video.addEventListener('enterpictureinpicture', event => {
console.log('Video is in Picture-in-Picture mode');
pipWindow = event.pictureInPictureWindow;
pipWindow.addEventListener('resize', resizeEvent);
});
video.addEventListener('leavepictureinpicture', event => {
console.log('Video leaving Picture-in-Picture mode');
pipWindow.removeEventListener('resize', resizeEvent);
});
Lastly, to avoid getting errors, you need to test first whether your browser supports the API. To do so you can simply check for the pictureUnPictureEnabled
property on the window.
if ('pictureInPictureEnabled' in document) {
pipButton.hidden = false;
} else {
pipButton.hidden = true;
}
The End Result
Now you can stream content to users even when they leave your site. (the tab must be kept open though) Trying to open a new element will terminate the previous one first.
Rocket Launch Your Career
Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies: