我有一个更广泛的问题,归结为这个同样的问题。我需要编写一个JS类来管理一个或多个(数量可以从1到无限变化)视频嵌入。后端系统是ExpressionEngine(但这在这里是不相关的)。主要目标是建立一个分析框架,将个体数据推送到我们的Adobe Analytics平台。这里仅显示了给出播放次数的部分,它可以从这里扩展很多。
CMS允许编辑在页面上创建模块来呈现视频。每个模块一个视频。每个模块基本上是通过Bootstrap 3排列的HTML部分(对于这个答案无关紧要)。
相关的HTML看起来像这样:
<div id="js_youTubeContainer_{innov_mod_ytplayer:id}" class="embed-responsive embed-responsive-16by9">
<div id="js_youTubeFrame_{innov_mod_ytplayer:id}" class="embed-responsive-item"></div>
</div>
这里说的"{innov_mod_ytplayer:id}"是我们CMS中的YouTube视频ID,它允许为每个嵌入项分配一个唯一的ID。稍后这非常重要。
在此之下,我会进行渲染:
var innovYouTube_{innov_mod_ytplayer:id} = new Ariba.Innovations.YouTube.Class({
'innovYouTubeVideoId': '{innov_mod_ytplayer:id}',
'innovYouTubeVideoTitle': '{innov_mod_ytplayer:title}',
'innovYouTubeDivId' : 'js_youTubeFrame_{innov_mod_ytplayer:id}'
});
innovYouTube_{innov_mod_ytplayer:id}.Init();
var onYouTubeIframeAPIReady = (function() {
try{
innovYouTube_{innov_mod_ytplayer:id}.config.functionCache = onYouTubeIframeAPIReady;
return function() {
try{
innovYouTube_{innov_mod_ytplayer:id}.onYouTubeIframeAPIReady();
var newOnYouTubeIframeAPIReady = innovYouTube_{innov_mod_ytplayer:id}.config.functionCache.apply(this, arguments);
return newOnYouTubeIframeAPIReady;
}catch(err){}
};
}catch(err){}
})();
你会在这里看到一些ExpressionEngine模板标记 - 这些只是YouTube的视频ID和视频标题。要复制此操作,您需要相应更改它们。
这样做的目的是允许我动态更新单个全局回调以获取每个新嵌入视频的新代码。最终,此回调将包含对其自己的类实例的调用。您需要那些try/catch块,因为它为除了“当前正在执行”的嵌入之外的所有“其他”嵌入引发了虚假正面错误 - 请记住,此脚本针对页面上的每个嵌入运行一次。这些错误是预期的,实际上并不会造成问题,因此try/catch将它们抑制。
使用CMS模板标记,我基于YouTube视频ID创建每个实例。如果有人添加了同一个视频模块多次,我可能会遇到问题,但是这是一个易于处理的业务问题,因为不应该发生这种情况。这使我能够为每个视频重复实例化我的类的唯一实例。
该脚本的关键部分基于这个极其有用的SO答案:
Adding code to a javascript function programmatically
这是实际的类。它主要是注释的...我们使用jQuery,因此您将在这里看到一个重要的用法,即$.extend()方法。我在类构造函数方法中使用它作为方便,但您也可以使用vanilla JS(
JavaScript equivalent of jQuery's extend method)。我只是发现jQuery更容易阅读,并且由于它对我可用,我使用它。
if (typeof Ariba === "undefined") { var Ariba = {}; }
if (typeof Ariba.Innovations === "undefined") { Ariba.Innovations = {}; }
if (typeof Ariba.Innovations.YouTube === "undefined") { Ariba.Innovations.YouTube = {}; }
if (typeof Ariba.Innovations.YouTube.Class === "undefined") {
Ariba.Innovations.YouTube.Class = function (config) {
this.static = {
'ytScriptId': 'js_youtubeFrameAPI',
'ytScriptUrl': 'https://www.youtube.com/iframe_api'
};
this.config = {
'adobeAnalyticsFired': false
};
this.config = $.extend(true, this.config, config);
this.config = $.extend(true, this.config, this.static);
this.config.this = this;
};
Ariba.Innovations.YouTube.Class.prototype.Init = function () {
this.config.apiScript = document.createElement('script');
this.config.apiScript.src = 'https://www.youtube.com/iframe_api';
this.config.apiScript.id = 'js_youtubeFrameAPI' + this.config.innovYouTubeVideoId;
this.config.firstScriptTag = document.getElementsByTagName('script')[0];
this.config.firstScriptTag.parentNode.insertBefore(this.config.apiScript, this.config.firstScriptTag);
}
Ariba.Innovations.YouTube.Class.prototype.onYouTubeIframeAPIReady = function (event) {
var _this = this;
this.config.ytPlayer = new YT.Player(this.config.innovYouTubeDivId, {
videoId: this.config.innovYouTubeVideoId,
events: {
'onReady': _this.onPlayerReady.bind(_this),
'onStateChange': _this.onPlayerStateChange.bind(_this)
}
});
}
Ariba.Innovations.YouTube.Class.prototype.onPlayerReady = function (event) {
}
Ariba.Innovations.YouTube.Class.prototype.onPlayerStateChange = function (event) {
if (event.data === YT.PlayerState.PLAYING && !this.config.adobeAnalyticsFired) {
this.config.adobeAnalyticsFired = true;
if (typeof _satellite !== "undefined") {
window._satellite.data.customVars.adhoc_tracker_val = "Innovations Video: " + this.config.innovYouTubeVideoTitle + " (" + this.config.innovYouTubeVideoId + ")";
_satellite.track('adhoctrack');
}
}
}
}
另外需要注意的几点:
一旦解决了主要的全局回调问题,将作用域保留在类实例中就很容易。你只需要添加.bind()即可。例如:
'onReady': _this.onPlayerReady.bind(_this)
您可能还会看到以下内容:
var _this = this
这样做是为了避免意外丢失实例的“this”作用域。也许不是必需的,但这是我多年来采用的惯例。
总之,我已经花了一个星期的时间在这个问题上,并且认为我应该与SO社区分享它,因为从我的寻找答案中清楚地表明,很多人都在寻找解决方案。
function createPlayer(playerInfo) { return new YT.Player(playerInfo.id, { videoId: playerInfo.videoId, events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } function onPlayerReady(event) { console.log('ready'); }
- Fawn