Youtube iframe api not triggering onYouTubeIframeA

2019-01-07 13:20发布

问题:

I've been battling with the youtube iframe api for quite some time now. Somehow the method onYouTubeIframeAPIReady is not always triggered.

From the symptoms it seems a loading problem. No errors are shown in the inspector.

Here is my code:

HTML

<div id="player"></div>
          <script>
            videoId = 'someVideoId';
            var tag = document.createElement('script');
            tag.src = "//www.youtube.com/iframe_api";
            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
          </script>

JS

(called at the end of the page. I tried to place the code right after the above script and the result was the same.)

var isReady = false
  , player
  , poster
  , video;

$(function () {
$('.js-play').click(function (e) {
    e.preventDefault();
    interval = setInterval(videoLoaded, 100);
  });
});
function onYouTubeIframeAPIReady() {
  console.log(videoId)
  player = new YT.Player('player', {
    height: '445',
    width: '810',
    videoId: videoId,
    events: {
      'onReady': onPlayerReady//,
      //'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerReady(event) {
  isReady = true;
  console.log("youtube says play")
}

function videoLoaded (){
  if (isReady) {
      console.log("ready and play")
      poster.hide();
      video.show();

      $('body').trigger('fluidvideos');

      player.playVideo();
      clearInterval(interval);
  } 
}

The problem is that sometimes nothing gets printed by the console.log and nothing happens.

On mobile phones this happens all the time. Any ideas?

回答1:

It is not a timeout issue, and you should not need to fire this function manually.

Make sure your onYouTubeIframeAPIReady function is available at the global level, not nested (hidden away) within another function.



回答2:

You can always append it to the window object to make sure it is evoked globally. This is helpful if your code is using amd.

window.onYouTubeIframeAPIReady = function() {}


回答3:

If you have to put in inside to a function, one possible solution is instead of:

function onYouTubeIframeAPIReady() {
  // func body...
}

you can declare it like this:

window.onYouTubeIframeAPIReady = function() {
  // func body...
}

In this case make sure, that you insert the certain script to the dom after the declaration (the insertBefore() call what is in the global scope now in your case):



回答4:

If you load the IFrame Player API code asynchronously as follows:

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

and you are also loading the script code like this:

<script src="http://www.youtube.com/player_api"></script>

then the function onYouTubeIframeAPIReady won't be called due to the same code is being loaded twice (the function is called just one time when the api is completely loaded).

So use just one of the ways according to your needs.



回答5:

Make sure you are testing the page by having it actually served on the web, not just loaded locally. YouTube iFrame API behavior is inconsistent/nondeterministic



回答6:

After revisiting this, I found a working solution for me when using webpack (or I assume any other commongJS module system), or if you find the youtube api is ready before your own JS file, was to use an interval - until youtube provides some form of promise callback:

var checkYT = setInterval(function () {
    if(YT.loaded){
        //...setup video here using YT.Player()

        clearInterval(checkYT);
    }
}, 100);

This seemed to be the most error-proof in my case.



回答7:

I had a similar problem, In my case onYouTubeIframeAPIReady was being called so fast that the actual implementation wasn't ready for the right call. So after youtube player_api call I added a simple implementation with a var for verification of onYouTubeIframeAPIReady just like that:

<script src="http://www.youtube.com/player_api"></script>
<script>
var playerReady = false
window.onYouTubeIframeAPIReady = function() { //simple implementation
    playerReady = true;
}
</script>
</header>
.
.
.
<body>
<script>
window.onYouTubeIframeAPIReady = function() { //real and fully implemented
    var player; // ... and so on ...
}
if(playerReady) { onYouTubeIframeAPIReady(); console.log("force to call again") }
</script>


回答8:

I actually made this to lazy load the youtube player. Seems bullet proof to me.

window.videoApiLoaded = [];
window.videoApiLoaded.youtube = false;

function loadYoutubeVideo(videoId) {

    window.onYouTubeIframeAPIReady = function() { document.dispatchEvent(new CustomEvent('onYouTubeIframeAPIReady', {})) };

    if(window.videoApiLoaded.youtube == false) {
        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        window.videoApiLoaded.youtube = true;
    }

    var player;

    document.addEventListener('onYouTubeIframeAPIReady', function (e) {
        player = new YT.Player('player', {
          height: '400',
          width: '600',
          videoId: videoId,
          events: {
            'onStateChange': onYtStateChange
          }
        });

    }, false);

}


回答9:

I was able to make this work under almost all circumstances with a simple setTimeout on load of the page. Not ideal, I know, but it is just another check that fixes the problem most of the time.

setTimeout(function(){
    if (typeof(player) == 'undefined'){
        onYouTubeIframeAPIReady();
    }
}, 3000)