DEV Community

Cover image for Instagram Like Play/Pause for Videos using React.js
faisal khan
faisal khan

Posted on • Edited on

Instagram Like Play/Pause for Videos using React.js

A study states that users browsing the internet have short attention spans.

Videos allow users to consume more content, at a faster pace. Users are much more apt to engage with videos than with static text.

Instagram has videos that play when in focus and pause when goes out of focus. I have been working on a similar feature to play and pause videos but for the web.

In this article, I will help you to achieve this functionality and on native and any third-party web video player.

Here, we will be using a youtube video player. But, it will work with any video player.

TLDR; Here is the code sandbox working example:

Tech used

I will be using Youtube API with React.js to solve the problem.

It can also be achieved by other JS libraries or frameworks.

Youtube API:
It helps load Youtube iframe on the DOM.
The iframe plays a youtube video and Youtube API gives the power to handle multiple video controls.

Ref:
[https://developers.google.com/youtube/iframe_api_reference]

React.js: It's one of the best libraries for frontend development.

Ref: [https://reactjs.org/]

                  Enough Talks Lets Code
Enter fullscreen mode Exit fullscreen mode

Lets Code

Steps:

1] Loading Youtube Script on DOM:

I have created a custom hook named useYoutubeScript which loads the script in our DOM and gives us a callback when done.

Please follow the comments in the code.

Edit playpauseyoutube


import { useState, useEffect } from "react";

// Iframe API Link
const YOUTUBE_PLAYER_API = "https://www.youtube.com/iframe_api";

export default function useYoutubeScript(scriptId) {
  // Keeping track of script loaded and error state
  const [state, setState] = useState({
    loaded: false,
    error: false
  });

  useEffect(
    () => {
      // if youtube is already available in window return.

      if (window.YT) {
        return [true, false];
      }

      // Create script tag, add ID and source to it.
      const script = document.createElement("script");
      script.id = scriptId;
      script.src = YOUTUBE_PLAYER_API;

      // Youtube promotes adding the script at the top.
      const firstScriptTag = document.getElementsByTagName("script")[0];
      firstScriptTag.parentNode.insertBefore(script, firstScriptTag);

      /* 
         Youtube API fires 'onYouTubeIframeAPIReady' when API 
         is loaded
      */

      window.onYouTubeIframeAPIReady = () => {
        // fire when script is loaded
        onScriptLoad();
      };

      const onScriptLoad = () => {
        setState({
          loaded: true,
          error: false
        });
      };

      const onScriptError = () => {
        setState({
          loaded: true,
          error: true
        });
      };

      // Listen when script has caused any error
      script.addEventListener("error", onScriptError);

      // Remove event listeners on cleanup
      return () => {
        script.removeEventListener("error", onScriptError);
      };
    },
    [scriptId] // Only re-run effect if script src changes
  );

  return [state.loaded, state.error];
}
Enter fullscreen mode Exit fullscreen mode

2] Embedding Youtube Iframe and Video player on DOM:

For embedding a youtube player we have copy video ID from youtube.

After that, we have to pass the video ID to the youtube video player instance.

You can read more about the youtube player parameters https://developers.google.com/youtube/player_parameters

Please follow the comments in the code.

import React, { useRef, useEffect } from "react";
import useYoutubeScript from "./useYoutubeScript";

export default function App() {
  const [loaded, error] = useYoutubeScript("sample-youtube");
  const isPlayerReady = useRef(false);
  const player = useRef(null);
  const isPlayerAlreadySet = player && player.current;
  const hasYoutubeInWindow = typeof window.YT === "object" && window.YT.Player;

  useEffect(() => {
    const playerObject = player.current;
    // destroy player when unmounting
    return () => playerObject && playerObject.destroy && playerObject.destroy();
  }, []);

  useEffect(() => {
    /* This useEffect runs when youtube script is loaded on 
       DOM.
    */
    if (!isPlayerAlreadySet && hasYoutubeInWindow) {
      /* create a Youtube player and attach it to a div with 
         ID, apply player parameters and callback events
      */
      player.current = new window.YT.Player("youtube-iframe-id", {
        videoId: "PvtI_71FrF8",
        width: 400,
        height: 350,
        events: {
          onReady: onPlayerReady
        }
      });
    }
  }, [loaded]);

  if (!loaded || error) {
    // show loading when loaded is false or error is true
    return <div>Loading...</div>;
  }

  // this function is fired when player is ready for playing
  const onPlayerReady = () => {
    if (isPlayerReady && !!isPlayerReady.current) {
      return;
    }
    /* 
       It's important to mute the video before playing 
       since the browser does not allow autoplay with 
       sound on
    */
    player.current.mute && player.current.mute();
    // set player ready to true
    isPlayerReady.current = true;
  };

  return (
    <div className="App">
      <div>
        <div id="youtube-iframe-id" />
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

3] Play and Pause Video based on Visibility:

Gone are the days we used to have scroll listener and complex calculation to detect if the component is in focus or not.

We have a new champ to help us out which is IntersectionObserver.

In short, it enables you to detect the visibility of an element, i.e. if it’s in the current viewport, and also the relative visibility of two elements in relationship to each other.

It does not run on the main thread hence does not hamper performance like a scroll listener used to.

Same as before we will be having a custom hook to detect the visibility of the component when a reference of the component is passed as a parameter to the hook.

The hook is named useIntersectionObserver.

Nowadays, most browsers support intersection-observer but still, there are some unsupported ones.

polyfill intersection observer

No worries we have you covered we have the polyfill for those browser's [https://www.npmjs.com/package/intersection-observer]

Please follow the comments in the code.

Edit playpauseyoutube

import { useEffect, useRef, useState } from 'react'

export default ({ root = null, rootMargin = '0px 0px 0px 0px', threshold = 0 }) => {
        // check if it running on client
    const isClient = typeof window === 'object'
    const [entry, updateEntry] = useState({})
    const [node, setNode] = useState(null)
    let observer = null

    if (isClient) {
        /*
           the root prop is of the parent element of the 
           component, if nothing is passed it will be the 
           viewport, the threshold is visibility percentage
        */
        observer = useRef(
            new window.IntersectionObserver(([intersectionEntry]) => updateEntry(intersectionEntry), {
                root,
                rootMargin,
                threshold
            })
        )
    }

    const unObserve = () => {
        const { current: currentObserver } = observer
        currentObserver.disconnect()
    }

    useEffect(() => {
        if (!isClient) {
            return false
        }
        const { current: currentObserver } = observer
        currentObserver.disconnect()

        if (node) currentObserver.observe(node)

        return () => currentObserver.disconnect()
    }, [node])

    return [setNode, entry, unObserve]
}

Enter fullscreen mode Exit fullscreen mode

Usage

const [ref,entry] = useIntersectionObserver({ threshold: 0.8 })
// Should be 80% in the viewport 

if(entry.isIntersecting){
// returns when its equal to or more than 80% in viewport
 playVideo()
} else {
// when less than 80% in viewport
 pauseVideo()
}

<div ref={ref} />
Enter fullscreen mode Exit fullscreen mode

Conclusion:

Creating a feed for the user includes multiple types of multimedia components like Images and Videos, where video auto-playing saves us a client interaction.

Intersection observer has solved multiple problems like lazy loading components, but video play pause is another really good use case for it.

Play and pause functionality can be achieved on any video player even on the native HTML5 video component.

Here is the code sandbox link -
https://codesandbox.io/s/playpauseyoutube-938i4

Top comments (0)