YouTube API - Firefox/IE return error “X is not a

2019-04-14 10:27发布

问题:

I've been pulling my hair out trying to get a fairly simple YouTube api integration working in FF/IE and have had no luck so far.

It sounds like either a scope issue or a call being made before the player is initialized but everything I've tried suggests it's not one of those two things. Also of note, everything works perfectly in (only) Chrome.

// Async api load per YT documentation...
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// Declare player and set basic functions...
var player

playVideo = function() { player.playVideo(); }
stopVideo = function() { player.stopVideo(); }

// YT Api ready function...
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    videoId: "TkJcg4bmAYs",
    events: {
      'onStateChange': onPlayerStateChange
    }
  });

  // After player object created, bind popup function to page anchors...
  var popup = $('#video-popup')
  ,   popupFrame = $(popup).children('.center')
  ;

  $('a[data-vid]').click(function(e){
    e.preventDefault ? e.preventDefault() : e.returnValue = false;

    var clicked = $(e.target).closest('a')
    ,   videoID = $(clicked).attr('data-vid')
    ;

    if (!$(popup).hasClass('working')){
      // If popup isn't already working, cue video and animate popup in... 
      player.cueVideoById({videoId:videoID});

      $(popup).addClass('working');
      $(popup).css('display','block').animate({
        opacity: '1'
      }, 200, function(){
        playVideo();
        $(popup).removeClass('working');
      });
    }
  });
}

Link to test page with the api and popup code, working in Chrome but not in other browsers. http://www.crackin.com/dev/regions/pathBuild/

回答1:

After imbedding half of my keyboard keys into my forehead, I finally stumbled across the frustratingly simple root of the problem... It's nothing but a visibility issue! ARRGG!!! Apparently, FireFox and IE do not have access to the player object if it is initialized while set to display:none;, but Chrome is smart enough to still initialize and target it even hidden.

The fix, in my particular case, was to load the popup as display:block with opacity:0, THEN initialize the api, THEN set the popup to display:none inside the YT api init function. Something like this:

First, CSS...

#video-popup { display: block; opacity: 0; }

.. then javascript...

var player
,   popup = $('#video-popup')
;

function onYouTubeIframeAPIReady() {
    player = new YT.Player('player', {
        videoId: "TkJcg4bmAYs"
    });
    $(popup).css('display','none');
}

Now I need to go get drunk.


ADDITIONAL STUFF: After finally figuring out the root of the problem, I was able to isolate some additional IE browser problems and work out solutions. These are just in case somebody else is having a similar issue, they may be able to carve out their own solution from this. My solution might be a bit situation-specific, but if anybody else is putting youtube videos in popups and needs IE support, this might help.

The fix above (setting display:none in the YT ready function) didn't seem to work perfectly for IE8, but was quickly fixed by wrapping it in a brief timeout:

function onYouTubeIframeAPIReady() {
    player = new YT.Player('player', {
        videoId: "TkJcg4bmAYs"
    });
    setTimeout(function(){
        $(popup).css('display','none');
    }, 500);
}

IE7 was a bit trickier, as it seems that if the player is EVER hidden using display:none, it immediately becomes unavailable regardless of whether it's been initialized or not.

Using conditional comments to apply classes to the tag, you can add some IE specific css to keep the popup "visible" but hide it offscreen instead.

#video-popup { position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: none; opacity: 0; }
.ie7Detect #video-popup { display: block; left: -101% }
#video-popup.open { left:0 !important; }

There is also an issue where in both IE7 and 8 the youtube player is still visible even when the parent container is set to opacity 0 (which happens while page is still loading). I tried adding rules to further target the contents of the popup and set their opacity also, but didn't work and wasn't super clean either. What I settled on was using a loading class.

So the popup has a loading class:

<div id="video-popup" class="loading">
    <!-- bunch of stuff in here -->
</div>

And function is updated...

function onYouTubeIframeAPIReady() {
    player = new YT.Player('player', {
        videoId: "TkJcg4bmAYs",
    });
    setTimeout(function(){
        $(popup).removeClass('loading');
    }, 500);
}

CSS looks something like this...

#video-popup { position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: none; opacity: 0; }
.ie7Detect #video-popup, #video-popup.loading { display: block; left: -100% }
#video-popup.open { left:0 !important; }

The last piece of the puzzle would be to incorporate a classname 'open' for your popup function when it's actually suppose to be visible.

Hope that helps somebody avoid some potential headaches in the future. On the downside, you may not be QUITE as enthusiastic to get drunk after addressing these issues as you might have otherwise been.

Peace, yo.



回答2:

First off, thanks for figuring this out. Like you, I think I tried every solution I could find to no avail.

Second, I'm writing because although my issue was very similar in nature, I was trying to load a video via the bxSlider jQuery plugin and I wanted to post my solution in hopes it could save someone else some time.

I simply just called a function on the onSliderLoad method. Here's what the function looks like:

function()
{
  if ($('.slideshow-item-video').length > 0) {

    $('.slideshow-item-video').each(function()
    {
      $(this).css('display', 'block');

      setTimeout(function()
      {
        $(this).css('display', 'none');
      }, 500);
    });
  }
}

I'm simply giving slides that contain a video a specific class and making them visible so the API can access the element, then setting a timeout to hide the element again.

This solution works in Firefox and IE8+. I have not figured out a solution for IE7, but, it's IE7.