我需要加载多个视频与youtube的API。这是我第一次使用它,所以我不确定我做错了什么,但这就是我正在尝试的:
var player;
var player2;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
videoId: 'hdy78ehsjdi'
});
player2 = new YT.Player('player', {
videoId: '81hdjskilct'
});
}
由于onYouTubeIframeAPIReady
函数应该只被调用一次,因此可以使用以下方法:
-
初始化并保存视频播放器信息(
ControlId,width,height,VideoId
) in array -
调用
onYouTubeIframeAPIReady
函数创建所有视频玩家
var playerInfoList = [{id:'player',height:'390',width:'640',videoId:'M7lc1UVf-VE'},{id:'player1',height:'390',width:'640',videoId:'M7lc1UVf-VE'}];
function onYouTubeIframeAPIReady() {
if(typeof playerInfoList === 'undefined')
return;
for(var i = 0; i < playerInfoList.length;i++) {
var curplayer = createPlayer(playerInfoList[i]);
}
}
function createPlayer(playerInfo) {
return new YT.Player(playerInfo.id, {
height: playerInfo.height,
width: playerInfo.width,
videoId: playerInfo.videoId
});
}
var playerInfoList = [{id:'player',height:'390',width:'640',videoId:'M7lc1UVf-VE'},{id:'player1',height:'390',width:'640',videoId:'M7lc1UVf-VE'}];
function onYouTubeIframeAPIReady() {
if(typeof playerInfoList === 'undefined')
return;
for(var i = 0; i < playerInfoList.length;i++) {
var curplayer = createPlayer(playerInfoList[i]);
}
}
function createPlayer(playerInfo) {
return new YT.Player(playerInfo.id, {
height: playerInfo.height,
width: playerInfo.width,
videoId: playerInfo.videoId
});
}
新YT的第一个参数。播放器需要是HTML元素的id(例如一个DIV)被替换为视频的iframe。当你对这两个对象都使用'player'时,你将把它们加载到同一个元素中。
<div id="ytplayer1"></div>
<div id="ytplayer2"></div>
<script>
var player;
var player2;
function onYouTubePlayerAPIReady() {
player = new YT.Player('ytplayer1', {
height: '390',
width: '640',
videoId: 'hdy78ehsjdi'
});
player2 = new YT.Player('ytplayer2', {
height: '390',
width: '640',
videoId: '81hdjskilct'
});
}
</script>
函数的参数在Youtube API文档中描述:
https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player(编辑:更改为右链接)
HTML
<div data-id="youtubevideoidhere" class="video"></div>
<div data-id="youtubevideoidhere" class="video"></div>
<div data-id="youtubevideoidhere" class="video"></div>
JS for Videos
// CREATE VIDEOS "CLASS" to handler videos
var Videos = (function() {
// VARIABLES
var $ = jQuery, // The jquery
players = [], // players array (to coltrol players individually)
queue = []; // videos queue (once api is ready, transform this into YT player)
// Constructor
function Videos() {}
// METHODS
// Add elements to queue
Videos.prototype.add = function($video) {
queue.push($video);
};
// Load YT API
Videos.prototype.loadApi = function() {
// jQuery get script
$.getScript("//www.youtube.com/iframe_api", function() {
// once loaded, create the onYouTubeIframeAPIReady function
window.onYouTubeIframeAPIReady = function() {
queue.forEach(function($video) {
// Create the YT player
var player = new YT.Player($video.get(0), {
'width': "100%",
'height': "100%",
'videoId': $video.data("id")
});
// add to players array
players.push(player);
});
};
});
};
return Videos;
})();
然后创建像这样的视频
var videos = new Videos();
$('.video').each( function () {
videos.add( $(this) );
})
videos.loadApi();
我有一个更广泛的问题,归结为同样的问题。我的要求是编写一个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(); // And... Go!
var onYouTubeIframeAPIReady = (function() {
try{ //wrap this in try/catch because it actually throws errors when it runs subsequent times - this is expected as it's related to YouTube "rerunning" the function on other videos.
innovYouTube_{innov_mod_ytplayer:id}.config.functionCache = onYouTubeIframeAPIReady; //cache the existing global function
return function() {
try{
innovYouTube_{innov_mod_ytplayer:id}.onYouTubeIframeAPIReady(); //execute this instance's function
var newOnYouTubeIframeAPIReady = innovYouTube_{innov_mod_ytplayer:id}.config.functionCache.apply(this, arguments); //add instances to global function
return newOnYouTubeIframeAPIReady; //update global function
}catch(err){}
};
}catch(err){}
})();
你也会在这里看到一些ExpressionEngine模板标签——那些只是来自YouTube的视频ID和视频标题。要复制这一点,您当然需要更改这些。
这样做是允许我动态更新单个全局回调与新代码为每个新嵌入的视频。最后,这个回调将包含对我的类的它们自己的实例的调用。您需要这些try/catch块,因为它会为所有"其他"嵌入抛出一个假阳性错误,除了它实际上正在执行的嵌入—记住这个脚本会为页面上的每个嵌入运行一次。错误是预期的,实际上不会引起问题,所以try/catch会抑制它们。
使用CMS模板标签,我基于YouTube视频ID创建每个实例。如果有人不止一次添加相同的视频模块,我会遇到问题,但这是一个很容易处理的业务问题,因为这不应该发生。这允许我为每个视频一遍又一遍地实例化我的类的唯一实例。
该脚本的关键部分是基于这个非常有用的SO答案:以编程方式向javascript函数添加代码
这是实际的类。评论最多的是…我们使用jQuery,因此您将在$.extend()方法中看到它的一个重要用途。我在类构造函数方法中使用它作为方便,但您也可以使用普通JS (JavaScript相当于jQuery's扩展方法)。我只是发现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") {//this script may be embedded more than once - do this to avoid re-processing it on subsequent loads
Ariba.Innovations.YouTube.Class = function (config) {
this.static = {
'ytScriptId': 'js_youtubeFrameAPI',
'ytScriptUrl': 'https://www.youtube.com/iframe_api'
};//static configuration. Will overwrite any other settings with the same name
this.config = {//optional configuration variables. Will be overridden by instance or static settings with the same name.
'adobeAnalyticsFired': false
};
this.config = $.extend(true, this.config, config);//inserts (destructively!) the instance settings.
this.config = $.extend(true, this.config, this.static);//inserts (destructively!) the static settings.
this.config.this = this;
};
Ariba.Innovations.YouTube.Class.prototype.Init = function () {
//Note: have to allow it to write it over an over because calling the API script is what makes YouTube call onYouTubeIframeAPIReady.
//if (document.getElementById('js_youtubeFrameAPI') === null) { // don't add the script again if it already exists!
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);
//}
//else { console.log("iframe script already embedded", this.config.innovYouTubeVideoId); }
}
Ariba.Innovations.YouTube.Class.prototype.onYouTubeIframeAPIReady = function (event) {
//console.log("onYouTubeIframeAPIReady", this.config.innovYouTubeVideoId, arguments);
var _this = this;
//console.log(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) {
//console.log("onPlayerReady", this.config.innovYouTubeVideoId, event);
}
Ariba.Innovations.YouTube.Class.prototype.onPlayerStateChange = function (event) {
//console.log("onPlayerStateChange", this.config.innovYouTubeVideoId, event, this);
if (event.data === YT.PlayerState.PLAYING && !this.config.adobeAnalyticsFired) {
//console.log("YouTube Video is PLAYING!!", this.config.innovYouTubeVideoId);
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社区分享一下,因为很明显,从我寻找的答案来看,很多其他人也在寻找这个问题的解决方案。
我需要在React中做同样的事情。扩展Vadim的答案,你可以做如下的事情,将它们添加到一个对象中,然后创建播放器,如果你不知道之前的播放器数组是什么样子的。
const YoutubeAPILoader = {
_queue: [],
_isLoaded: false,
load: function (component) {
// if the API is loaded just create the player
if (this._isLoaded) {
component._createPlayer()
} else {
this._queue.push(component)
// load the Youtube API if this was the first component added
if (this._queue.length === 1) {
this._loadAPI()
}
}
},
_loadAPI: function () {
// load the api however you like
loadAPI('//youtube.com/player_api')
window.onYouTubeIframeAPIReady = () => {
this._isLoaded = true
for (let i = this._queue.length; i--;) {
this._queue[i]._createPlayer()
}
this._queue = []
}
}
}
我所做的加载多个视频是破坏iframe时,我点击视频外部(你可以使用你想要的事件),然后我再次创建了div,所以你可以重复使用另一个视频ID
<iframe title="YouTube video player" src="https:YOUR CHANNEL Full Link" width="560" height="315" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
作为Vadim回答的补充,以下方法对我处理事件很有效:
const iframes = [{id: 'hello'},...];
const inOnReadyScope = "I can be accessed by onPlayerReady"
function onYouTubeIframeAPIReady() {
for (let i = 0; i < iframes.length; i++) {
const player = new YT.Player(iframe.id, {
events {
onReady: onPlayerReady
}
}
function onPlayerReady(event){
event.target.setVolume(0);
console.log(inOnReadyScope)
// use anything on event
}
}
}
<script type="text/javascript">
$(document).ready(function () {
$(".youtube-player").each(function () {
var playerid = $(this).attr("id");
setTimeout(function () {
onYouTubeIframeAPIReady2(playerid);
}, 2000);
});
});
function onYouTubeIframeAPIReady2(PlayerID) {
var ctrlq = document.getElementById(PlayerID);
console.log(ctrlq);
var player = new YT.Player(PlayerID, {
height: ctrlq.dataset.height,
width: ctrlq.dataset.width,
events: {
'onReady': function (e) {
e.target.cueVideoById({
videoId: ctrlq.dataset.video,
startSeconds: ctrlq.dataset.startseconds,
endSeconds: ctrlq.dataset.endseconds
});
}
}
});
}
</script>