stUrb
stUrb

Reputation: 6842

Youtube iframe API does not work as expected

In one of my modals I want to display a youtube video. Which one (which ID) depends on which button is used to open the model.

<button class='yt-play' data-yt='xxxxxx'>Play video</button>

In my javascript file I'm using the YT player-api to generate an iframe; i followed the Getting started on google developers.

So In the modal I added an <div id='player'></div> and This is my included javascript:

var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;

function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    videoId: '5ulO97zuVF0', //- just a temporary id
  });
}

// on document ready do some jQuery things, 
// like adding an event handler to the button. 
$(document).ready(function (){

   $('.yt-play').click(function(ev){
        ev.preventDefault();
        var videoid = $(this).data('yt');

        player.loadVideoById(videoid);
        $('#yt-player').modal('show');
        player.playVideo();

    });

});

The click-handler on yt-play should load the video by means of player.loadVideoById() as stated here in the documentation.

But somehow I get an javascript error: TypeError: player.loadVideoById is not a function

If I dump the player-object in the console I'm getting a nice player object; which holds amongst many others the loadVideoById function. At least it looks like it:

console screenshot

What's the reason the new video is not loaded?

Upvotes: 1

Views: 4898

Answers (5)

weblaunchuk
weblaunchuk

Reputation: 441

Only 6 years late to the party, I have the solution.

The issue is that the YouTube API doesn't like the DOM shuffling Foundation does when it opens a modal.

The only way to achieve this is to create the YouTube player after opening the modal:

function play_video(ytid) {
    
    modal = new Foundation.Reveal($('#yt_modal'),{});
    modal.open();

    ytplayer = new YT.Player('ytplayer', {
        videoId: ytid,
        height: '390',
        width: '640',
        playerVars: {
            'playsinline': 1
        },
        events: {
            'onReady': whatever()
        }
    });
}

This assumes

<div id="ytplayer"></div>

is inside your Foundation modal.

Oh and you'll need to remove the iframe each time else YouTube API just looks for the old iframe (which isn't where it expects, cos the modal is closed and the DOM has changed):

$(document).on('closed.zf.reveal', function(e) {
        switch($(e.target).prop('id')) {
            case 'yt_modal':
                ytplayer.destroy();
                break;
        }
    });

And regarding @Tosh's answer about the API returning a reference to the iframe not the player is not true as far as I can determine by comparing it to what's returned by onReady(ev.target) - they appear identical (so don't go down that blind alley like I did!)

Upvotes: 0

Tosh
Tosh

Reputation: 1857

The other answers don't clarify the issue.

The YouTube api is confusing for sure! This is because the YT.Player constructer returns a dom reference to the iframe of the youtube player not to the YT player object.

In order to get a reference to the YT player we need to listen to the onReady event.

var ytPlayer;
var videoIframe = new YT.Player('player', {
                        videoId: 'xxxxxxx',  // youtube video id
                        playerVars: {
                                'autoplay': 0,
                                'rel': 0,
                                'showinfo': 0
                        },
                        events: {
                           'onStateChange': onPlayerStateChange,
                           'onReady': function (ev) {
                                ytPlayer = ev.target;
                            }
                        }
});

Upvotes: 0

Alex Lomakin
Alex Lomakin

Reputation: 433

The code:

<script>
    // load API
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    // define player
    var player;
    function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
            height: '360',
            width: '640'
        });
    }

    $(function () {

       // load video and add event listeners
       function loadVideo(id, start, end) {
          // check if player is defined
          if ((typeof player !== "undefined")) {
            // listener for player state change
            player.addEventListener('onStateChange', function (event) {
                if (event.data == YT.PlayerState.ENDED) {
                    // do something
                }
            });
            // listener if player is ready (including methods, like loadVideoById
            player.addEventListener('onReady', function(event){
                event.target.loadVideoById({
                    videoId: id,
                    startSeconds: start,
                    endSeconds: end
                });
                // pause player (my specific needs)
                event.target.pauseVideo();
            });
        }
        // if player is not defined, wait and try again
        else {
            setTimeout(loadVideo, 100, id, start, end);
        }
      }

      // somewhere in the code
      loadVideo('xxxxxxxx', 0, 3);
      player.playVideo();
   });
</script>

Upvotes: 0

user2463050
user2463050

Reputation: 1

  1. add div element in html document
  2. initial YT to global object in onYouTubeIframeAPIReady function
  3. call YT object in your function

Upvotes: 0

Stu
Stu

Reputation: 11

It's possibly because the "loadVideoById" is not yet available.

You must construct your YT.Player Object with an events object, and include an "onReady" Event callback.

Then in your "onReady" callback function you bind your Button click event.

function onPlayerReady() {
    $('.yt-play').click(function(ev){
        ev.preventDefault();
        var videoid = $(this).data('yt');

        // player.loadVideoByID is now available as a function
        player.loadVideoById(videoid);

        $('#yt-player').modal('show');
        player.playVideo();
    });
}
player = new YT.Player('player', {
    videoId: '5ulO97zuVF0', //- just a temporary id,
    events:{
        “onReady”: onPlayerReady   
    }
});

See the events section in the docs for more:

https://developers.google.com/youtube/iframe_api_reference#Events

Upvotes: 1

Related Questions