-1

The following JavaScript works great when I have only one instance of a YouTube video on a page:

function createVideo(playerElement, videoID, autoplay=false) {
        const youtubeScriptId = 'youtube-api';
        const youtubeScript = document.getElementById(youtubeScriptId);

        if (youtubeScript === null) {
          const tag = document.createElement('script');
          const firstScript = document.getElementsByTagName('script')[0];

          tag.src = 'https://www.youtube.com/iframe_api';
          tag.id = youtubeScriptId;
          firstScript.parentNode.insertBefore(tag, firstScript);
        }

        window.onYouTubeIframeAPIReady = function() {
          return new window.YT.Player(playerElement, {
            videoId: videoID,
            playerVars: {
              autoplay: autoplay,
              modestbranding: 1,
              rel: 0
            }
          });
        }
      }

But when I try to call this again, the video doesn't load. Only the first video. This method is called via a click event after the page has loaded, and I pass in a videoID data attribute and build my new YouTube video to show on the page.

I assume because my JavaScript is creating only one instance on the window object, and not separate multiple instances. After further research I can see that the onYouTubeIframeAPIReady() method only fires once on a page, so that explains why the subsequent calls when the click event fires this method fails. Since it is impossible for me to know how many exact instances to load on a page, how would I refactor this code to make it dynamic, so that unlimited instances can be created and played on a page via click events only? I've seen countless tutorials of building YouTube videos when you know the elements on a page. But in this scenario either I do not know what is on the DOM, or want to slow the site down on page load by building unlimited YT videos to be inserted into the DOM only to probably not be clicked to play by the user.

Is it even possible to dynamically create a new YouTube video on a page using their YT API bound to a click event? From what I am reading online, it looks like a person has to load all videos at once when onYouTubeIframeAPIReady() is first loaded on the page after the YT API loads, and I have to add extra logic in place to play/stop each and then bind additional click events per each video to show/hide/play/stop. This is going to increase my page load dramatically, and add a bunch of JS overhang to my page. I simply want to create a new video on a click event.

stvar
  • 6,551
  • 2
  • 13
  • 28
Zach Smith
  • 5,490
  • 26
  • 84
  • 139

3 Answers3

0

I hope you're not using the same videoID every time, because for multiple instances of anything you need to have some kind of sequential ID management, like this...

var ID=[], Vid;


for(var i=0; i<20; i++){  // create 20 video players

 ID.push('V'+i);

 Vid=document.createElement('video');

 Vid.id=ID[i];

 document.body.appendChild(Vid);

 createVideo(e, ID[i], false);   // Your function

}

You get the drift...

  • On page load I will never know how many videos to be created, so hard setting it to `20` isn't going to work for my specific application. The `playerElement` having the iframe created and sitting inside is a class that is unique to the dom where the function is called. So with this all said, I'm not sure if your provided code will fit my situation. – Zach Smith Jun 29 '20 at 18:58
0

The core issue that you're experiencing is the YT.Player can only be initalized once on the page - and if you have multiple videos to be handled via the YT.Player you'd have to iterate over the videos one by one with the same YT.Player instance. It's a little weird at first, but can work - especially if you need to handle the videos via popup modals, etc.

Here's an example that I've used to iterate over a page that has multiple hidden modals with videos, and then handle the click events and playing the videos:

jQuery(document).ready(function ($) {
    let modalTriggerElements = $('.video_play_icon'),
        playerInfoList = [],
        players = []

    if (typeof (YT) == 'undefined' || typeof (YT.Player) == 'undefined') {
        let tag = document.createElement('script'),
            firstScript = document.getElementsByTagName('script')[0]

        tag.src = 'https://www.youtube.com/iframe_api';
        tag.id = 'youtube-api';
        firstScript.parentNode.insertBefore(tag, firstScript)
    }

    if (modalTriggerElements.length > 0) {
        $.each(modalTriggerElements, (index, element) => {
            buildPlayersList(element)
            modalTriggerClickEvent(element)
        })
    }

    window.onYouTubePlayerAPIReady = function () {
        if (typeof playerInfoList === 'undefined') return;

        for (let i = 0; i < playerInfoList.length; i++) {
            players[i] = createPlayer(playerInfoList[i]);
        }
    }

    function createPlayer(playerInfo) {
        return new YT.Player(playerInfo.playerId, {
            videoId: playerInfo.videoId,
            playerVars: {
                showinfo: 0,
            }
        });
    }

    function buildPlayersList(element) {
        let $modelDiv = $(element).closest('.hc_module').next('.video_model'),
            $playerDiv = $($modelDiv).find('#video-player');

        playerInfoList.push({
            'videoId': $($modelDiv).data('video-id'),
            'playerId': $($playerDiv)[0],
        });
    }

    function modalTriggerClickEvent(element) {
        $(element).on('click', () => {
            let $parentDiv = $(element).closest('.hc_module'),
                $nearestVideoDiv = $parentDiv.next('.video_model'),
                $closeDiv = $nearestVideoDiv.find('.close_modal')

            $nearestVideoDiv.css('display', 'block')
            $nearestVideoDiv.attr('aria-hidden', 'false')

            $closeDiv.on('click', () => {
                $nearestVideoDiv.css('display', 'none')
                $nearestVideoDiv.attr('aria-hidden', 'true')

                players.forEach((el) => {
                    el.stopVideo();
                });
            })
        })
    }
});
Zach Smith
  • 5,490
  • 26
  • 84
  • 139
-1

When you don't know how many players you can use random numbers to avoid conflicts...

var ID=[];  // Global array


function AddVideoPlayer(){

var Vid, i=ID.length-1;

 ID.push('V'+RandomNumber(99999999)); 

 Vid=document.createElement('video');

 Vid.id=ID[i];

 document.body.appendChild(Vid);

 createVideo(e, ID[i], false);   // Your function

}
  • The core issue here is that I need to dynamically create a YT video using the YT API bound to a click event, and not load them all at once on a page when `onYouTubeIframeAPIReady()` is first called. If this is even possible?? Regardless, I believe your answer does not meet the core concept of the question at hand. – Zach Smith Jun 29 '20 at 22:11