skip to Main Content

I’m a beginner with javascript and I managed to merge two functions controlling video play, however, they interfere with each other a bit.

This function is supposed to provide custom pause and play video controls

// VIDEO CONTROLS START
const video = document.getElementById("heroVideo"),
  pauseControl = document.getElementById("pauseControl"),
  playControl = document.getElementById("playControl"),
  playPauseButton = document.getElementById("playPauseButton");

playPauseButton.addEventListener("click", function () {
  if (video.paused) {
    video.play();
    playPauseButton.classList.remove("play");
    playPauseButton.classList.add("pause");
    pauseControl.style.display = "unset";
    playControl.style.display = "none";
  } else {
    video.pause();
    playPauseButton.classList.remove("pause");
    playPauseButton.classList.add("play");
    pauseControl.style.display = "none";
    playControl.style.display = "unset";
    video.removeAttribute("controls");
  }
});
// VIDEO CONTROLS END

And this one pauses and plays video automaticaly when out of user’s view field (so it does not play on the background)

// VIDEO OFFLOAD START
function videoOffload() {
  // Get all video elements with the "video-offload" class
  const videos = document.querySelectorAll(".video-offload");

  // Function to handle the Intersection Observer for a single video
  function handleVideoIntersection(video) {
    // Define the options for the Intersection Observer
    const options = {
      root: null, // Use the viewport as the root
      rootMargin: "0px", // No margin
      threshold: 0.1, // 10% of the target element must be visible to trigger
    };

    // Callback function when the video enters or exits the viewport
    const callback = (entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          // The video is in the viewport, so play it and show controls
          video.play();
          video.removeAttribute("controls");
        } else {
          // The video is out of the viewport, so pause it and hide controls
          video.pause();
        }
      });
    };

    // Create an Intersection Observer with the specified options and callback for the current video
    const observer = new IntersectionObserver(callback, options);

    // Start observing the current video element
    observer.observe(video);
  }

  // Iterate over all video elements and apply the Intersection Observer
  videos.forEach((video) => {
    video.setAttribute("autoplay", "false"); // Disable autoplay initially

    // Apply Intersection Observer to the current video
    handleVideoIntersection(video);
  });
}

// Call the videoOffload function to initialize
videoOffload();

// VIDEO OFFLOAD END

Video control button

  <button id="playPauseButton" class="play">
              <i id="playControl" class="fa-regular fa-circle-play fa-xl"></i>
              <i id="pauseControl" class="fa-regular fa-circle-pause fa-xl"></i>
            </button>

The issue is, as soon as I pause the video manually (utilising the first function) scroll out of the field of view of the video and then scroll back to the video again it resumes playing (the second function overcalls the first one). That is fine, however, the video control icons are reversed and you need to click twice – to pause and then play according to the pause & play function logic (because they change on click and the video was triggered by another function).

They are both in the same js file. I tried to keep them separately but the issue persisted.

I also tried to inject the icons to the HTML according to the IF statements – playPauseButton.innerHTML = "<i id='pauseControl' class='fa-regular fa-circle-pause fa-xl'></i>"; that did not work however.

The solution crossing my mind is to have them as indicators of the current state of the video – playing or paused, so no matter how the video was triggered, it is going to display the right icon OR make those two functions not interfere with each other.

What do you think is the best solution and code?

2

Answers


  1. Chosen as BEST ANSWER

    Okay, I made the icon functionality to work with using the event listener for a video play or pause. I also did some tweaks with a state variable that tracks whether the video was paused by the user or by the videoOffload() as advised. Right now the video pause and play works well, however, the visible icon is only the default pause one, not switching to play after pausing the video. So this statement would not been executed:

     pauseControl.style.display = "none";
     playControl.style.display = "unset";
    
    // VIDEO CONTROLS START
    const video = document.getElementById("heroVideo"),
      pauseControl = document.getElementById("pauseControl"),
      playControl = document.getElementById("playControl"),
      playPauseButton = document.getElementById("playPauseButton");
    
    let userPaused = false; // Introduce a state variable
    
    function updateControls() {
      if (video.paused) {
        playPauseButton.classList.remove("pause");
        playPauseButton.classList.add("play");
        pauseControl.style.display = "none";
        playControl.style.display = "unset";
        video.removeAttribute("controls");
      } else {
        playPauseButton.classList.remove("play");
        playPauseButton.classList.add("pause");
        pauseControl.style.display = "unset";
        playControl.style.display = "none";
      }
    }
    
    video.addEventListener('pause', function() {
      if (!userPaused) {
        updateControls();
      }
    });
    
    video.addEventListener('play', function() {
      if (!userPaused) {
        updateControls();
      }
    });
    
    playPauseButton.addEventListener("click", function() {
      if (video.paused) {
        userPaused = false;
        video.play();
      } else {
        userPaused = true;
        video.pause();
      }
    });
    // VIDEO CONTROLS END
    // VIDEO OFFLOAD START
    function videoOffload() {
      // Get all video elements with the "video-offload" class
      const videos = document.querySelectorAll(".video-offload");
      // Function to handle the Intersection Observer for a single video
      function handleVideoIntersection(video) {
        // Define the options for the Intersection Observer
        const options = {
          root: null, // Use the viewport as the root
          rootMargin: "0px", // No margin
          threshold: 0.1, // 10% of the target element must be visible to trigger
        };
        // Callback function when the video enters or exits the viewport
        const callback = (entries, observer) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              // The video is in the viewport, so play it and show controls
              if (!userPaused) {
                video.play();
                video.removeAttribute("controls");
              }
            } else {
              // The video is out of the viewport, so pause it and hide controls
              if (!userPaused) {
                video.pause();
              }
            }
          });
        };
        // Create an Intersection Observer with the specified options and callback for the current video
        const observer = new IntersectionObserver(callback, options);
        // Start observing the current video element
        observer.observe(video);
      }
      // Iterate over all video elements and apply the Intersection Observer
      videos.forEach((video) => {
        video.setAttribute("autoplay", "false"); // Disable autoplay initially
    
        // Apply Intersection Observer to the current video
        handleVideoIntersection(video);
      });
    }
    videoOffload();
    // VIDEO OFFLOAD END
    
    

  2. Consider refactoring the controls management to an event listener on the video element. This will be the most accurate source-of-truth to whether the video is playing or not:

    function updateControls() {
      if (video.paused) {
        playPauseButton.classList.remove("pause");
        playPauseButton.classList.add("play");
        pauseControl.style.display = "none";
        playControl.style.display = "unset";
        video.removeAttribute("controls");
      } else {
        playPauseButton.classList.remove("play");
        playPauseButton.classList.add("pause");
        pauseControl.style.display = "unset";
        playControl.style.display = "none";
      }
    } 
        
    video.addEventListener('pause', updateControls);
    video.addEventListener('play', updateControls);
    
    playPauseButton.addEventListener("click", function() {
      if (video.paused) {
        video.play();
      } else {
        video.pause();
      }
    });
    
    // VIDEO CONTROLS START
    const video = document.getElementById("heroVideo"),
      pauseControl = document.getElementById("pauseControl"),
      playControl = document.getElementById("playControl"),
      playPauseButton = document.getElementById("playPauseButton");
      
    function updateControls() {
      if (video.paused) {
        playPauseButton.classList.remove("pause");
        playPauseButton.classList.add("play");
        pauseControl.style.display = "none";
        playControl.style.display = "unset";
        video.removeAttribute("controls");
      } else {
        playPauseButton.classList.remove("play");
        playPauseButton.classList.add("pause");
        pauseControl.style.display = "unset";
        playControl.style.display = "none";
      }
    } 
        
    video.addEventListener('pause', updateControls);
    video.addEventListener('play', updateControls);
    
    playPauseButton.addEventListener("click", function() {
      if (video.paused) {
        video.play();
      } else {
        video.pause();
      }
    });
    // VIDEO CONTROLS END
    
    // VIDEO OFFLOAD START
    function videoOffload() {
      // Get all video elements with the "video-offload" class
      const videos = document.querySelectorAll(".video-offload");
    
      // Function to handle the Intersection Observer for a single video
      function handleVideoIntersection(video) {
        // Define the options for the Intersection Observer
        const options = {
          root: null, // Use the viewport as the root
          rootMargin: "0px", // No margin
          threshold: 0.1, // 10% of the target element must be visible to trigger
        };
    
        // Callback function when the video enters or exits the viewport
        const callback = (entries, observer) => {
          entries.forEach((entry) => {
            if (entry.isIntersecting) {
              // The video is in the viewport, so play it and show controls
              video.play();
              video.removeAttribute("controls");
            } else {
              // The video is out of the viewport, so pause it and hide controls
              video.pause();
            }
          });
        };
    
        // Create an Intersection Observer with the specified options and callback for the current video
        const observer = new IntersectionObserver(callback, options);
    
        // Start observing the current video element
        observer.observe(video);
      }
    
      // Iterate over all video elements and apply the Intersection Observer
      videos.forEach((video) => {
        video.setAttribute("autoplay", "false"); // Disable autoplay initially
    
        // Apply Intersection Observer to the current video
        handleVideoIntersection(video);
      });
    }
    
    // Call the videoOffload function to initialize
    videoOffload();
    
    // VIDEO OFFLOAD END
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    
    <video id="heroVideo" class="video-offload" loop controls>
      <source src="https://v3.cdnpk.net/videvo_files/video/free/2013-08/large_preview/hd0992.mp4" type="video/mp4">
      <source src="https://v3.cdnpk.net/videvo_files/video/free/2013-08/large_watermarked/hd0992_preview.mp4" type="video/mp4">
    </video>
    
    <button id="playPauseButton" class="play">
      <i id="playControl" class="fa-regular fa-circle-play fa-xl"></i>
      <i id="pauseControl" class="fa-regular fa-circle-pause fa-xl"></i>
    </button>
    
    <div style="height: 150vh"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search