YouTube iframe API:如何控制 HTML 中已有的 iframe 播放器?
我希望能够控制基于 iframe 的 YouTube 播放器.该播放器已经在 HTML 中,但我想通过 JavaScript API 控制它们.
I want to be able to control iframe based YouTube players. This players will be already in the HTML, but I want to control them via the JavaScript API.
我一直在阅读 iframe API 文档,其中介绍了如何添加新视频使用 API 访问页面,然后使用 YouTube 播放器功能对其进行控制:
I've been reading the documentation for the iframe API which explain how to add a new video to the page with the API, and then control it with the YouTube player functions:
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('container', {
height: '390',
width: '640',
videoId: 'u1zgFlCw8Aw',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
该代码创建一个新的播放器对象并将其分配给播放器",然后将其插入到#container div 中.然后我可以对'player'进行操作,并在其上调用playVideo()
、pauseVideo()
等.
That code creates a new player object and assigns it to 'player', then inserts it inside the #container div. Then I can operate on 'player' and call playVideo()
, pauseVideo()
, etc. on it.
但我希望能够对页面上已经存在的 iframe 播放器进行操作.
我可以用旧的嵌入方法很容易地做到这一点,比如:
I could do this very easily with the old embed method, with something like:
player = getElementById('whateverID');
player.playVideo();
但这不适用于新的 iframe.如何分配页面上已有的 iframe 对象,然后在其上使用 API 函数?
But this doesn't work with the new iframes. How can I assign a iframe object already on the page and then use the API functions on it?
推荐答案
Fiddle 链接:源代码 - 预览 - 小版本
更新:这个小函数只会在一个方向上执行代码.如果您想要全面支持(例如事件侦听器/获取器),请查看 在 jQuery 中监听 Youtube 事件
Fiddle Links: Source code - Preview - Small version
Update: This small function will only execute code in a single direction. If you want full support (eg event listeners / getters), have a look at Listening for Youtube Event in jQuery
作为深入代码分析的结果,我创建了一个函数:function callPlayer
请求对任何带框的 YouTube 视频进行函数调用.请参阅 YouTube Api 参考以获取可能的函数调用的完整列表.阅读源代码中的注释以获得解释.
As a result of a deep code analysis, I've created a function: function callPlayer
requests a function call on any framed YouTube video. See the YouTube Api reference to get a full list of possible function calls. Read the comments at the source code for an explanation.
在 2012 年 5 月 17 日,代码大小翻了一番,以照顾玩家的就绪状态.如果您需要一个不处理播放器就绪状态的紧凑函数,请参阅 http://jsfiddle.net/8R5y6/.
On 17 may 2012, the code size was doubled in order to take care of the player's ready state. If you need a compact function which does not deal with the player's ready state, see http://jsfiddle.net/8R5y6/.
/**
* @author Rob W <gwnRob@gmail.com>
* @website https://stackoverflow.com/a/7513356/938089
* @version 20190409
* @description Executes function on a framed YouTube video (see website link)
* For a full list of possible functions, see:
* https://developers.google.com/youtube/js_api_reference
* @param String frame_id The id of (the div containing) the frame
* @param String func Desired function to call, eg. "playVideo"
* (Function) Function to call when the player is ready.
* @param Array args (optional) List of arguments to pass to function func*/
function callPlayer(frame_id, func, args) {
if (window.jQuery && frame_id instanceof jQuery) frame_id = frame_id.get(0).id;
var iframe = document.getElementById(frame_id);
if (iframe && iframe.tagName.toUpperCase() != 'IFRAME') {
iframe = iframe.getElementsByTagName('iframe')[0];
}
// When the player is not ready yet, add the event to a queue
// Each frame_id is associated with an own queue.
// Each queue has three possible states:
// undefined = uninitialised / array = queue / .ready=true = ready
if (!callPlayer.queue) callPlayer.queue = {};
var queue = callPlayer.queue[frame_id],
domReady = document.readyState == 'complete';
if (domReady && !iframe) {
// DOM is ready and iframe does not exist. Log a message
window.console && console.log('callPlayer: Frame not found; id=' + frame_id);
if (queue) clearInterval(queue.poller);
} else if (func === 'listening') {
// Sending the "listener" message to the frame, to request status updates
if (iframe && iframe.contentWindow) {
func = '{"event":"listening","id":' + JSON.stringify(''+frame_id) + '}';
iframe.contentWindow.postMessage(func, '*');
}
} else if ((!queue || !queue.ready) && (
!domReady ||
iframe && !iframe.contentWindow ||
typeof func === 'function')) {
if (!queue) queue = callPlayer.queue[frame_id] = [];
queue.push([func, args]);
if (!('poller' in queue)) {
// keep polling until the document and frame is ready
queue.poller = setInterval(function() {
callPlayer(frame_id, 'listening');
}, 250);
// Add a global "message" event listener, to catch status updates:
messageEvent(1, function runOnceReady(e) {
if (!iframe) {
iframe = document.getElementById(frame_id);
if (!iframe) return;
if (iframe.tagName.toUpperCase() != 'IFRAME') {
iframe = iframe.getElementsByTagName('iframe')[0];
if (!iframe) return;
}
}
if (e.source === iframe.contentWindow) {
// Assume that the player is ready if we receive a
// message from the iframe
clearInterval(queue.poller);
queue.ready = true;
messageEvent(0, runOnceReady);
// .. and release the queue:
while (tmp = queue.shift()) {
callPlayer(frame_id, tmp[0], tmp[1]);
}
}
}, false);
}
} else if (iframe && iframe.contentWindow) {
// When a function is supplied, just call it (like "onYouTubePlayerReady")
if (func.call) return func();
// Frame exists, send message
iframe.contentWindow.postMessage(JSON.stringify({
"event": "command",
"func": func,
"args": args || [],
"id": frame_id
}), "*");
}
/* IE8 does not support addEventListener... */
function messageEvent(add, listener) {
var w3 = add ? window.addEventListener : window.removeEventListener;
w3 ?
w3('message', listener, !1)
:
(add ? window.attachEvent : window.detachEvent)('onmessage', listener);
}
}
用法:
callPlayer("whateverID", function() {
// This function runs once the player is ready ("onYouTubePlayerReady")
callPlayer("whateverID", "playVideo");
});
// When the player is not ready yet, the function will be queued.
// When the iframe cannot be found, a message is logged in the console.
callPlayer("whateverID", "playVideo");
可能的问题(和答案):
问:不行!
A:不起作用"不是一个清晰的描述.您收到任何错误消息吗?请出示相关代码.
Possible questions (& answers):
Q: It doesn't work!
A: "Doesn't work" is not a clear description. Do you get any error messages? Please show the relevant code.
Q:playVideo
不播放视频.
A:播放需要用户交互,并且 iframe 上存在 allow="autoplay"
.请参阅 https://developers.google.com/web/updates/2017/09/autoplay-policy-changes 和 https:///developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
Q: playVideo
does not play the video.
A: Playback requires user interaction, and the presence of allow="autoplay"
on the iframe. See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes and https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
Q:我使用 <iframe src="http://www.youtube.com/embed/As2rZGPGKDY"/>
嵌入了一个 YouTube 视频,但是该函数不执行任何函数!
A:您必须在 URL 的末尾添加 ?enablejsapi=1
:/embed/vid_id?enablejsapi=1
.
Q: I have embedded a YouTube video using <iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />
but the function doesn't execute any function!
A: You have to add ?enablejsapi=1
at the end of your URL: /embed/vid_id?enablejsapi=1
.
Q:我收到错误消息指定了无效或非法的字符串".为什么?
A:API 在本地主机 (file://
) 上无法正常运行.在线托管您的(测试)页面,或使用 JSFiddle.示例:请参阅此答案顶部的链接.
Q: I get error message "An invalid or illegal string was specified". Why?
A: The API doesn't function properly at a local host (file://
). Host your (test) page online, or use JSFiddle. Examples: See the links at the top of this answer.
问:你是怎么知道的?
A:我花了一些时间来手动解释 API 的源代码.我得出结论,我必须使用 postMessage
方法.为了知道要传递哪些参数,我创建了一个拦截消息的 Chrome 扩展程序.扩展的源代码可以在这里下载.
Q: How did you know this?
A: I have spent some time to manually interpret the API's source. I concluded that I had to use the postMessage
method. To know which arguments to pass, I created a Chrome extension which intercepts messages. The source code for the extension can be downloaded here.
问:支持哪些浏览器?
A:所有支持 JSON 和 postMessage
.
Q: What browsers are supported?
A: Every browser which supports JSON and postMessage
.
- IE 8+
- Firefox 3.6+(实际上是 3.5,但
document.readyState
是在 3.6 中实现的) - Opera 10.50+
- Safari 4+
- Chrome 3+
- IE 8+
- Firefox 3.6+ (actually 3.5, but
document.readyState
was implemented in 3.6) - Opera 10.50+
- Safari 4+
- Chrome 3+
相关答案/实现:使用jQuery淡入带框视频一个>
完整的 API 支持:在 jQuery 中监听 Youtube 事件
官方 API:https://developers.google.com/youtube/iframe_api_reference
- 2012 年 5 月 17 日
已实现onYouTubePlayerReady
:callPlayer('frame_id', function() { ... })
.
当播放器尚未准备好时,函数会自动排队. - 2012 年 7 月 24 日
已在受支持的浏览器中更新并成功测试(向前看). - 2013 年 10 月 10 日当函数作为参数传递时,
callPlayer
会强制检查准备情况.这是必需的,因为当callPlayer
在插入 iframe 之后在文档准备好时立即调用时,它无法确定 iframe 是否完全准备好.在 Internet Explorer 和 Firefox 中,这种情况会导致过早调用postMessage
,而被忽略. - 2013 年 12 月 12 日,建议在 URL 中添加
&origin=*
. - 2014 年 3 月 2 日,撤回了将
&origin=*
删除到 URL 的建议. - 2019 年 4 月 9 日,修复了在页面准备好之前加载 YouTube 时导致无限递归的错误.添加有关自动播放的说明.
- 17 may 2012
ImplementedonYouTubePlayerReady
:callPlayer('frame_id', function() { ... })
.
Functions are automatically queued when the player is not ready yet. - 24 july 2012
Updated and successully tested in the supported browsers (look ahead). - 10 october 2013
When a function is passed as an argument,
callPlayer
forces a check of readiness. This is needed, because whencallPlayer
is called right after the insertion of the iframe while the document is ready, it can't know for sure that the iframe is fully ready. In Internet Explorer and Firefox, this scenario resulted in a too early invocation ofpostMessage
, which was ignored. - 12 Dec 2013, recommended to add
&origin=*
in the URL. - 2 Mar 2014, retracted recommendation to remove
&origin=*
to the URL. - 9 april 2019, fix bug that resulted in infinite recursion when YouTube loads before the page was ready. Add note about autoplay.
相关文章