YouTube iframe API:如何控制已经在HTML中的iframe播放器?
我想要能够控制基于iframe的YouTube播放器。 这些玩家将已经在HTML中,但我想通过JavaScript API来控制它们。
我一直在阅读iframe API的文档,该文档解释了如何使用API向页面添加新视频,然后使用YouTube播放器功能控制它:
var player; function onYouTubePlayerAPIReady() { player = new YT.Player('container', { height: '390', width: '640', videoId: 'u1zgFlCw8Aw', events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); }
该代码创建一个新的玩家对象,并将其分配给“玩家”,然后将其插入到#container div中。 然后我可以在“播放器”上操作,并在其上调用playVideo()
, pauseVideo()
等。
但是我希望能够在已经在页面上的iframe播放器上进行操作。
我可以很容易地用旧的嵌入方法来做到这一点,例如:
player = getElementById('whateverID'); player.playVideo();
但是这不适用于新的iframe。 我怎样才能分配页面上的iframe对象,然后使用它的API函数?
小提琴链接: 源代码 – 预览 – 小版本
更新:这个小函数将只执行一个方向的代码。 如果你想完全支持(例如事件监听器/获取器),请看jQuery中的Listening for Youtube Event
作为深入的代码分析的结果,我创建了一个函数: function callPlayer
在任何带框的YouTube视频上请求函数调用。 请参阅YouTube Api参考以获取可能的函数调用的完整列表。 阅读源代码中的注释以获得解释。
2012年5月17日,为了照顾玩家的准备状态,代码大小加倍。 如果你需要一个不涉及玩家就绪状态的紧凑函数,请参阅http://jsfiddle.net/8R5y6/ 。
/** * @author Rob W <gwnRob@gmail.com> * @website https://stackoverflow.com/a/7513356/938089 * @version 20131010 * @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 / 0 = 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 (!domReady || iframe && (!iframe.contentWindow || queue && !queue.ready) || (!queue || !queue.ready) && 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");
可能的问题(&答案):
问 :这是行不通的!
答 :“不起作用”不是一个明确的描述。 你有什么错误信息吗? 请显示相关的代码。
问 :我已经使用<iframe src="http://www.youtube.com/embed/As2rZGPGKDY" />
嵌入了YouTube视频,但该功能不会执行任何功能!
答 :您必须在URL末尾添加?enablejsapi=1
: /embed/vid_id?enablejsapi=1
。
问 :我收到错误消息“指定了无效或非法的字符串”。 为什么?
答 :API在本地主机( file://
)上无法正常工作。 在线托管您的(测试)页面,或使用JSFiddle 。 示例:请参阅此答案顶部的链接。
问 :你怎么知道这个?
答 :我花了一些时间来手动解释API的来源。 我的结论是我必须使用postMessage
方法。 要知道哪些参数通过,我创建了一个截取消息的Chrome扩展。 扩展的源代码可以在这里下载。
问 :哪些浏览器支持?
答 :支持JSON和postMessage
每个浏览器。
- IE 8+
- Firefox 3.6+(实际上3.5,但
document.readyState
在3.6中实现) - 歌剧10.50 +
- Safari 4+
- Chrome 3+
相关的答案/实现: 使用jQuery淡入一个带框的视频
完整的API支持: 在jQuery中监听Youtube事件
官方API: https : //developers.google.com/youtube/iframe_api_reference
修订记录
- 2012年5月17日
实现callPlayer('frame_id', function() { ... })
:callPlayer('frame_id', function() { ... })
。
当玩家尚未准备好时,功能会自动排队。 - 2012年7月24日
在支持的浏览器中更新并进行了测试(展望未来)。 - 2013年10月10日当函数作为参数传递时,
callPlayer
强制检查准备情况。 这是必要的,因为当文档准备就绪时插入iframe之后调用callPlayer
时,它无法确定iframe是否已经准备就绪。 在Internet Explorer和Firefox中,这种情况导致过早地调用postMessage
,这被忽略了。 - 2013年12月12日,建议在URL中添加
&origin=*
。 - 2014年3月2日,收回的建议删除
&origin=*
到URL。
看起来YouTube已经更新了他们的JS API,所以这个默认是可用的! 您可以使用现有的YouTube iframe的ID …
<iframe id="player" src="http://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1&origin=http://example.com" frameborder="0"></iframe>
…在你的JS …
var player; function onYouTubeIframeAPIReady() { player = new YT.Player('player', { events: { 'onStateChange': onPlayerStateChange } }); } function onPlayerStateChange() { //... }
…和构造函数将使用您现有的iframe,而不是用新的替换它。 这也意味着你不必指定videoId给构造函数。
请参阅加载视频播放器
你可以用少得多的代码来做到这一点:
function callPlayer(func, args) { var i = 0, iframes = document.getElementsByTagName('iframe'), src = ''; for (i = 0; i < iframes.length; i += 1) { src = iframes[i].getAttribute('src'); if (src && src.indexOf('youtube.com/embed') !== -1) { iframes[i].contentWindow.postMessage(JSON.stringify({ 'event': 'command', 'func': func, 'args': args || [] }), '*'); } } }
工作示例: http : //jsfiddle.net/kmturley/g6P5H/296/
我自己的版本的金T的代码上面,它结合了一些jQuery,并允许针对特定的内联框架。
$(function() { callPlayer($('#iframe')[0], 'unMute'); }); function callPlayer(iframe, func, args) { if ( iframe.src.indexOf('youtube.com/embed') !== -1) { iframe.contentWindow.postMessage( JSON.stringify({ 'event': 'command', 'func': func, 'args': args || [] } ), '*'); } }