Video Element Picture-In-Picture in JavaScript Tutorial

In this tutorial, I will show how to Create a Picture-In-Picture Feature for your website's videos. The Picture-In-Picture is a feature that allows your user to stream the video within an inset window on your screen. With this, the user can continue reading the other content of the page while the video still playing in a window of your screen. Users can also continue to stream the video using the said feature while they are at the other window.

Here, I will be providing a simple web application that can contain a Picture-In-Picture Feature without using the built-in control for this feature in the video Element. The goal of the application is to have a custom button where the user triggers the Picture-In-Picture Feature. The application will also trigger the said feature when the page is scrolled while playing the video.

Getting Started:

I am using Bootstrap v5 on this simple application for the design of the user interface. Also, I am using jQuery Library. You can download it using links that I provided below.

Please compile the Framework and Library's files in your source code directory. In my case, all css files are compiled into a css directory and js files into a js directory.

Creating the Interface

Now, we will be creating the main template interface of the application. Create a new HTML File in your source code folder naming index.html. Next, open the file into your preferred text editors such as notepad++ and sublime text. Copy/Paste the HTML Script below and save it. The file contains the script of the elements in our application intarface including the video element. Make sure that the imported external CSS and JS file paths are correct according to the locations of the files on your end.

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.     <meta charset="UTF-8">
  4.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Video Picture-in-Picture</title>
  7.     <link rel="stylesheet" href="./Font-Awesome-master/css/all.min.css">
  8.     <link rel="stylesheet" href="./css/bootstrap.min.css">
  9.     <link rel="stylesheet" href="./css/custom.css">
  10.     <script src="./js/jquery-3.6.0.min.js"></script>
  11.     <script src="./js/bootstrap.min.js"></script>
  12.     <script src="./js/script.js"></script>
  13. </head>
  14.  
  15. <body class="bg-light">
  16.     <nav class="navbar navbar-expand-lg navbar-dark bg-dark bg-gradient" id="topNavBar">
  17.         <div class="container">
  18.             <a class="navbar-brand" href="https://sourcecodester.com">
  19.             Sourcecodester
  20.             </a>
  21.  
  22.             <div>
  23.                 <b class="text-light">Video Picture-in-Picture</b>
  24.             </div>
  25.         </div>
  26.     </nav>
  27.     <div class="container py-5" id="page-container">
  28.         <!-- Video Container -->
  29.         <div id="vid-container">
  30.             <video src="sample-mp4-file.mp4" id="vid-el" class="w-100" controls></video>
  31.         </div>
  32.         <!-- End of Video Container -->
  33.         <!-- Picture in Picture Button -->
  34.         <div class="text-center">
  35.             <button class="btn btn-primary btn-lg px-4" type="button" id="btnPiP">Enable Picture-In-Picture</button>
  36.         </div>
  37.         <!-- End of Picture in Picture Button -->
  38.         <h3 class="text-center"><b>Try to play the video and scroll down</b></h3>
  39.         <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sagittis mauris eros, sit amet commodo ipsum aliquam a. Aliquam rutrum interdum accumsan. Fusce vel suscipit libero. Vestibulum tristique risus purus, eget interdum velit eleifend eu.
  40.             Mauris malesuada faucibus odio, non eleifend nibh mattis eget. Vivamus commodo maximus tellus, ac mollis urna ultricies sit amet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam scelerisque
  41.             turpis sit amet euismod semper. Aenean sed maximus justo. Sed commodo suscipit metus sed interdum. Maecenas condimentum mollis tortor, quis scelerisque sem efficitur ullamcorper. Maecenas varius est a enim dictum, auctor auctor velit sagittis.
  42.             Cras congue feugiat ultricies.
  43.             <br><br> Praesent ligula leo, pharetra id lacinia eu, semper et sapien. Duis eleifend ex sem, in rhoncus libero hendrerit bibendum. Morbi luctus nibh sit amet blandit eleifend. Vestibulum sodales tellus eget mauris tempor interdum. Nulla ornare
  44.             at libero nec lobortis. Ut at eleifend neque, sed cursus augue. In hac habitasse platea dictumst. Phasellus pharetra gravida diam, ut finibus orci bibendum ut. Praesent sed sagittis ante. Maecenas varius diam at nibh ultricies accumsan. Vestibulum
  45.             commodo felis eu magna convallis, non feugiat lectus elementum. Ut aliquam magna id nisl dapibus posuere. Cras id justo bibendum, scelerisque elit ac, pellentesque diam.
  46.             <br><br> In faucibus metus non augue ornare, et dictum est laoreet. Suspendisse finibus tellus eget pellentesque consectetur. Vestibulum at accumsan massa, ac gravida tellus. Vivamus dignissim leo sed condimentum congue. Quisque auctor dapibus
  47.             ante, quis interdum neque suscipit eu. Fusce sed imperdiet odio. Nulla sed orci eu erat eleifend fringilla ac ut risus. Donec vitae lacus tincidunt, scelerisque nisi sed, faucibus odio.
  48.             <br><br> Aliquam erat volutpat. Sed consequat tempor sem ut rutrum. Fusce ac egestas sapien. Sed sit amet felis ut nibh scelerisque sollicitudin in vitae nunc. Pellentesque cursus, leo nec sollicitudin suscipit, quam justo rutrum augue, quis
  49.             posuere risus turpis vitae leo. Aliquam luctus velit ac leo rhoncus interdum. Etiam gravida congue tortor et scelerisque. In at lorem urna. Etiam iaculis quam sit amet metus suscipit pellentesque non ut dolor. Nullam at orci id risus volutpat
  50.             fermentum vitae in nisi. Curabitur maximus scelerisque mauris vel imperdiet. Fusce leo nulla, cursus eget massa elementum, rutrum lobortis erat. Donec vehicula nec augue non consequat.
  51.             <br><br> Donec venenatis orci sed pharetra vestibulum. Vestibulum laoreet, nisl eu tristique convallis, eros magna finibus nunc, elementum eleifend mauris magna nec velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra,
  52.             per inceptos himenaeos. Aenean luctus blandit justo faucibus placerat. Fusce venenatis ornare purus, nec lobortis nisi eleifend pretium. Aliquam erat volutpat. Vestibulum et iaculis libero. Morbi aliquam turpis at risus suscipit, non tincidunt
  53.             dui elementum. Mauris malesuada luctus orci in mattis. Nulla euismod feugiat odio, a hendrerit nunc commodo et. Nam mollis risus sapien, ut scelerisque lorem lobortis ac. Fusce suscipit iaculis enim vel lobortis. Aenean ut molestie magna.
  54.             Phasellus a tortor in mauris ultrices rhoncus.</p>
  55.  
  56.     </div>
  57. </body>
  58. </html>

Creating the Custom CSS

Next, we will be creating a CSS File that contains the CSS scripts for the custom design of some elements in our interface. Save this file as custom.css. In my case, this file is located inside the CSS directory. Make sure to import the file in the index.html file.

  1. :root {
  2.     --bs-success-rgb: 71, 222, 152 !important;
  3. }
  4.  
  5. html,
  6. body {
  7.     height: 100%;
  8.     width: 100%;
  9.     font-family: Apple Chancery, cursive;
  10. }
  11.  
  12. .btn-info.text-light:hover,
  13. .btn-info.text-light:focus {
  14.     background: #000;
  15. }
  16.  
  17. #vid-el {
  18.     height: 60vh;
  19.     background: linear-gradient(0deg, #3c3b3b, #424242, #000000);
  20.     object-fit: unset;
  21.     object-position: center center;
  22. }

Creating the Custom JavaScript

Lastly, we will create an external JavaScript File which contains the all the event listeners and script to achieve the Picture-in-Picture Feature into our application. Save this file as script.js and in my case this file is located inside my js directory. Make sure also to import this file in the index.html file.

  1. var videoEl, pipBtn, isPlay;
  2. $(function() {
  3.     videoEl = $('#vid-el')
  4.     pipBtn = $('#btnPiP')
  5.         // Check if the browser supports a picture in picture
  6.     if ('pictureInPictureEnabled' in document) {
  7.  
  8.         // Picture In Picture Button Event Listener
  9.         pipBtn.click(function() {
  10.             if (!pipBtn.hasClass('is-enabled')) {
  11.                 videoEl[0].requestPictureInPicture().catch(err => {
  12.                     console.error(err)
  13.                 })
  14.  
  15.             } else {
  16.                 console.log('close pip')
  17.                 document.exitPictureInPicture().catch(err => {
  18.                     console.error(err)
  19.                 })
  20.             }
  21.         })
  22.  
  23.         // Event Lister for Video enters in Picture-in-Picture
  24.         videoEl[0].addEventListener('enterpictureinpicture', function() {
  25.             pipBtn.text("Exit Pincture-in-Picture").addClass('is-enabled')
  26.             console.log('PiP Enabled')
  27.             videoEl[0].play()
  28.  
  29.         })
  30.  
  31.         // Event Lister for Video leaving in Picture-in-Picture
  32.         videoEl[0].addEventListener('leavepictureinpicture', function() {
  33.             pipBtn.text("Enable Picture-In-Picture").removeClass('is-enabled')
  34.             console.log('Leaved the PiP')
  35.             videoEl[0].pause()
  36.         })
  37.  
  38.         // Following Event Listener is for forcing users to make click gestures to prevent PIP API Gesture Error
  39.  
  40.         // Event Listener when the video plays
  41.         videoEl[0].addEventListener('playing', function() {
  42.             isPlay = true
  43.             console.log(isPlay)
  44.  
  45.         })
  46.  
  47.         // Event Listener when the video has paused
  48.         videoEl[0].addEventListener('pause', function() {
  49.             isPlay = false
  50.             console.log(isPlay)
  51.         })
  52.  
  53.         // Enter and Leave Picture in Picture when scrolling when Video is playing
  54.         $(window).on('scroll', function() {
  55.             $(window).trigger('focus')
  56.             var vidElOffset = videoEl.offset().top + (videoEl.height() * .3)
  57.             var currentPos = $(window).scrollTop()
  58.             if (isPlay === true) {
  59.                 if (currentPos > vidElOffset) {
  60.                     videoEl[0].requestPictureInPicture().catch(err => {
  61.                         console.error(err)
  62.                     })
  63.                 } else {
  64.                     if (document.pictureInPictureElement)
  65.                         document.exitPictureInPicture().catch(err => {
  66.                             console.error(err)
  67.                         })
  68.                 }
  69.             }
  70.         })
  71.  
  72.     } else {
  73.         // Disabling the PIP Button if not supported
  74.         pipBtn.text("Picture-In-Picture is Not Supported").attr('disabled', true)
  75.     }
  76.  
  77. })

That's it!. Now you can test the application on your side and see if it works as we planned.

DEMO VIDEO

If you encountered an error on your end, feel free to leave a comment below. You can also download the working source code that I provided for this tutorial. The download button is located below this article.

That's the end of this tutorial. I hope you'll find tutorial useful for your future Web Application Projects. Explore more on this website for more Free Source Codes and Tutorials.

Enjoy Coding :)

Add new comment