jribeiro
jribeiro

Reputation: 3463

Youtube iframe api not triggering onYouTubeIframeAPIReady

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?

Upvotes: 48

Views: 73346

Answers (10)

T. Vuong
T. Vuong

Reputation: 51

I have a similar problem - Inconsistent triggering of onYoutubeIframeAPIReady as described in the issue (as opposed to not triggering). The problem would disappear whenever developer tools are turned on. The issue seems to be the order of loading the iframe_api script.

If iframe_api is loading before the handler is defined, it may trigger so quickly that by the time the handler is defined, it's already gone. The fix is to get the trigger function defined before attempting to load the script. So rather than do

<script src="https://www.youtube.com/iframe_api"></script>
<script>
function onYouTubeIframeAPIReady() {
...
}
</script>

I just change the order of loading/defining handler

<script>
function onYouTubeIframeAPIReady() {
...
}
</script>
<script src="https://www.youtube.com/iframe_api"></script>

Upvotes: 0

Matt Loye
Matt Loye

Reputation: 1311

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);

}

Upvotes: 2

Joar
Joar

Reputation: 224

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.

Upvotes: 10

bcm
bcm

Reputation: 5500

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.

Upvotes: 74

Joao
Joao

Reputation: 2746

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.

Upvotes: 9

Diogo Garcia
Diogo Garcia

Reputation: 594

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>

Upvotes: 1

Ben Wheeler
Ben Wheeler

Reputation: 7374

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

Upvotes: 3

Pengő Dzs&#243;
Pengő Dzs&#243;

Reputation: 865

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):

Upvotes: 15

Erick R Soto
Erick R Soto

Reputation: 1421

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() {}

Upvotes: 60

nosforz
nosforz

Reputation: 39

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)

Upvotes: -7

Related Questions