2

我正在使用 YouTube iFrame API 在页面上嵌入许多视频。此处的文档:https ://developers.google.com/youtube/iframe_api_reference#Requirements

总之,您可以使用以下代码段异步加载 API:

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

加载后,API 会触发预定义的回调函数onYouTubePlayerAPIReady

有关其他上下文:我在 Google Closure 中为此定义了一个库文件。我提供了一个命名空间:goog.provide('yt.video');

然后我使用goog.exportSymbol以便 API 可以找到该函数。这一切都很好。

我的挑战是我想将 2 个变量传递给回调函数。如果不在对象的上下文中定义这两个变量,有什么方法可以做到这一点window

goog.provide('yt.video');

goog.require('goog.dom');

yt.video = function(videos, locales) {
  this.videos = videos;
  this.captionLocales = locales;

  this.init();
};

yt.video.prototype.init = function() {
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};

/*
 * Callback function fired when YT API is ready
 * This is exported using goog.exportSymbol in another file and
 * is being fired by the API properly.
 */
 yt.video.prototype.onPlayerReady = function(videos, locales) {
    window.console.log('this :' + this); //logs window
    window.console.log('this.videos : ' + this.videos); //logs undefined
    /*
     * Video settings from Django variable
     */
    for(i=0; i<this.videos.length; i++) {
      var playerEvents = {};
      var embedVars = {};

      var el = this.videos[i].el;
      var playerVid = this.videos[i].vid;
      var playerWidth = this.videos[i].width;
      var playerHeight = this.videos[i].height;
      var captionLocales = this.videos[i].locales;
      if(this.videos[i].playerVars)
        var embedVars = this.videos[i].playerVars;
      }
      if(this.videos[i].events) {
        var playerEvents = this.videos[i].events;
      }

      /*
       * Show captions by default
       */
      if(goog.array.indexOf(captionLocales, 'es') >= 0) {
        embedVars.cc_load_policy = 1;
      };

      new YT.Player(el, {
        height: playerHeight,
        width: playerWidth,
        videoId: playerVid,
        events: playerEvents,
        playerVars: embedVars
    });
 };

};

为了初始化这一点,我目前在自执行匿名函数中使用以下内容:

  var videos = [
    {"vid": "video_id", "el": "player-1", "width": 640, "height": 390, "locales": ["es", "fr"], "events": {"onStateChange": stateChanged}}, 
    {"vid": "video_id", "el": "player-2", "locales": ["es", "fr"], "width": 640, "height": 390}
  ];

  var locales = ['es'];

  var videoTemplate = new yt.video(videos, locales);
4

2 回答 2

2

如何定义onYouTubePlayerAPIReady为一个全局函数,就像 API 所期望的那样,然后onPlayerReady从该函数内部调用您的方法?代码示例:

window.onYouTubePlayerAPIReady = function () {
    var args = Array.prototype.slice.call(arguments);
    args.push(videos, locales);
    videoTemplate.onPlayerReady.apply(videoTemplate, args);
};

并且您修改 onPlayerReady 方法的签名以以相同的顺序接受参数

于 2012-06-11T03:54:49.030 回答
1

To answer your specific question:

My challenge is that I would like to pass 2 variables to the callback function. Is there any way to do this without defining these 2 variables in the context of the window object?

Yes, there are ways to provide data to a callback function without using global variables. However, before looking at the callback function, the YouTube API has the following requirement:

In addition, any HTML page that contains the YouTube player must implement a JavaScript function named onYouTubePlayerReady. The API will call this function when the player is fully loaded and the API is ready to receive calls.

The YouTube API examples add event listeners within the onYouTubePlayerReady function as shown here:

function onYouTubePlayerAPIReady() {
  var player;
  player = new YT.Player('player', {
    width: 1280,
    height: 720,
    videoId: 'u1zgFlCw8Aw',
    events: {
      'onReady': onPlayerReady,
      'onPlaybackQualityChange': onPlayerPlaybackQualityChange,
      'onStateChange': onPlayerStateChange,
      'onError': onPlayerError
    }
  });
} 

It appears that your example callback function yt.video.prototype.onPlayerReady is intended to be an event listener for the YouTube API event onReady emitted by a YouTube YT.Player object. The API docs indicate that event listeners are added either when constructing the YT.Player object or added using the addEventListener function (the Closure Library alternative is goog.events.listen).

Since the yt.video.prototype.onPlayerReady event listener needs to "listen" for the onReady event emitted from a YouTube YT.Player object, it would be circular to construct new YT.Player instances within the event listener yt.video.prototype.onPlayerReady intended to listen for an event emitted by a YT.Player instance.

Getting back to the original question of providing data to callback functions without using global variables, you cannot pass arbitrary function arguments to an event listener (for example, in the original code sample above, yt.video.prototype.onPlayerReady must accept an Event object as its first argument rather than a videos array). However, the event listener yt.video.prototype.onPlayerReady can be used as a callback function and still access the state of yt.video instance objects as long as it is bound to instances of yt.video.

One way to bind the this argument to a specific object is with the Closure Library function goog.bind(functionToCall, selfObject, var_args). The yt.video constructor could be modified as follows:

goog.provide('yt.video');

goog.require('goog.dom');

/**
 * @constructor
 */
yt.video = function(videos, locales) {
  this.videos = videos;
  this.captionLocales = locales;

  this.onPlayerReadyListener = goog.bind(this.onPlayerReady, this);

  this.init();
};

yt.video.prototype.init = function() {
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
};

/*
 * Callback function fired when YT API is ready
 * This is exported using goog.exportSymbol in another file and
 * is being fired by the API properly.
 */
yt.video.prototype.onPlayerReady = function(event) {
  // Logs [object Object]
  window.console.log('this :' + this);

  // Logs [object Object],[object Object]
  window.console.log('this.videos : ' + this.videos); 

  event.target.playVideo();
};


Then the bound event listener could be added to a YT.Player object as follows:

var myVideoObject = new yt.video(videos, locales);

function onYouTubePlayerAPIReady() {
  var player;
  player = new YT.Player('player', {
    width: 1280,
    height: 720,
    videoId: 'u1zgFlCw8Aw',
    events: {
      'onReady': myVideoObject.onPlayerReadyListener
    }
  });
} 
于 2012-06-11T04:14:54.597 回答