YouTube iframe API:我如何控制已经在HTML中的iframe播放器?

时间:2021-08-24 18:56:36

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的YouTube播放器。这些播放器已经在HTML中了,但是我想通过JavaScript API来控制它们。

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:

我一直在阅读iframe API的文档,它解释了如何使用API将新视频添加到页面中,然后用YouTube播放器的功能来控制它:

var player;
function onYouTubePlayerAPIReady() {
    player = new YT.Player('container', {
        height: '390',
        width: '640',
        videoId: 'u1zgFlCw8Aw',
        events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
        }
    });
}

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.

该代码创建一个新的player对象并将其分配给“player”,然后将其插入到#container div中。然后我可以操作“player”,并调用playVideo()、pauseVideo()等。

But I want to be able to operate on iframe players which are already on the page.

但是我希望能够对已经在页面上的iframe播放器进行操作。

I could do this very easily with the old embed method, with something like:

我可以很容易地用旧的嵌入方法,比如:

player = getElementById('whateverID');
player.playVideo();

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?

但这与新的iframe不兼容。我如何在页面上指定一个iframe对象,然后使用它的API函数?

4 个解决方案

#1


286  

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

小提琴链接:源代码-预览-小版本更新:这个小的函数只会在一个方向上执行代码。如果您想要完全支持(如事件监听器/ getter),请查看一下jQuery中的Youtube事件。

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.

由于进行了深入的代码分析,我创建了一个函数:函数callPlayer请求在任何框架的YouTube视频上调用函数。请参阅YouTube Api参考以获得可能的函数调用的完整列表。请阅读源代码中的注释以获得解释。

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/.

在2012年5月17日,为了照顾玩家的就绪状态,代码的大小增加了一倍。如果您需要一个不处理播放器就绪状态的紧凑函数,请参见http://jsfiddle.net/8R5y6/。

/**
 * @author       Rob W <gwnRob@gmail.com>
 * @website      https://*.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);
    }
}

Usage:

用法:

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");

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.

问:它不工作!A:“不工作”不是一个清晰的描述。你收到错误消息了吗?请出示相关代码。

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.

问:我已经嵌入了一个YouTube视频,使用

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.

问:我得到错误消息“指定无效或非法字符串”。为什么?答:API在本地主机上不能正常工作(文件://)。把你的(测试)页面放在网上,或者使用JSFiddle。例子:看看这个答案顶部的链接。

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.

问:你是怎么知道的?答:我花了一些时间来手动解释API的源代码。我的结论是,我必须使用postMessage方法。为了知道要传递哪些参数,我创建了一个Chrome扩展,它拦截消息。扩展的源代码可以在这里下载。

Q: What browsers are supported?
A: Every browser which supports JSON and postMessage.

问:支持哪些浏览器?A:每个支持JSON和postMessage的浏览器。

  • IE 8+
  • IE 8 +
  • Firefox 3.6+ (actually 3.5, but document.readyState was implemented in 3.6)
  • Firefox 3.6+(实际上是3.5,但是文档。readyState在3.6中实现
  • Opera 10.50+
  • Opera 10.50 +
  • Safari 4+
  • Safari 4 +
  • Chrome 3+
  • Chrome 3 +

Related answer / implementation: Fade-in a framed video using jQuery
Full API support: Listening for Youtube Event in jQuery
Official API: https://developers.google.com/youtube/iframe_api_reference

相关的答案/实现:使用jQuery的完整API支持在框架视频中使用:在jQuery官方API中监听Youtube事件:https://developers.google.com/youtube/iframe_api_reference。

Revision history

  • 17 may 2012
    Implemented onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Functions are automatically queued when the player is not ready yet.
  • 2012年5月17日执行onYouTubePlayerReady: callPlayer('frame_id', function(){…})。当玩家还没有准备好时,函数会自动排队。
  • 24 july 2012
    Updated and successully tested in the supported browsers (look ahead).
  • 2012年7月24日在受支持的浏览器中进行了更新和成功测试(展望未来)。
  • 10 october 2013 When a function is passed as an argument, callPlayer forces a check of readiness. This is needed, because when callPlayer 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 of postMessage, which was ignored.
  • 2013年10月10日,当一个函数作为参数传递时,callPlayer强制检查是否准备就绪。这是需要的,因为当callPlayer在iframe插入的时候被调用,而文档已经准备好了,它不能确定iframe已经完全准备好了。在Internet Explorer和Firefox中,这一场景导致了对postMessage的过早调用,这被忽略了。
  • 12 Dec 2013, recommended to add &origin=* in the URL.
  • 2013年12月12日,建议在URL中添加&origin=*。
  • 2 Mar 2014, retracted recommendation to remove &origin=* to the URL.
  • 2014年3月2日,撤回建议删除&来源=*到URL。

#2


29  

Looks like YouTube has updated their JS API so this is available by default! You can use an existing YouTube iframe's ID...

看起来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>

...in your JS...

…JS中……

var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    events: {
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerStateChange() {
  //...
}

...and the constructor will use your existing iframe instead of replacing it with a new one. This also means you don't have to specify the videoId to the constructor.

…构造函数将使用您现有的iframe,而不是用新的iframe替换它。这也意味着您不必为构造函数指定视频。

See Loading a video player

请参见加载视频播放器。

#3


13  

You can do this with far less code:

你可以用更少的代码来做到这一点:

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 || []
            }), '*');
        }
    }
}

Working example: http://jsfiddle.net/kmturley/g6P5H/296/

工作示例:http://jsfiddle.net/kmturley/g6P5H/296/

#4


3  

My own version of Kim T's code above which combines with some jQuery and allows for targeting of specific iframes.

我自己版本的Kim T的代码结合了一些jQuery并允许针对特定的iframe。

$(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 || []
        } ), '*');
    }
}

#1


286  

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

小提琴链接:源代码-预览-小版本更新:这个小的函数只会在一个方向上执行代码。如果您想要完全支持(如事件监听器/ getter),请查看一下jQuery中的Youtube事件。

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.

由于进行了深入的代码分析,我创建了一个函数:函数callPlayer请求在任何框架的YouTube视频上调用函数。请参阅YouTube Api参考以获得可能的函数调用的完整列表。请阅读源代码中的注释以获得解释。

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/.

在2012年5月17日,为了照顾玩家的就绪状态,代码的大小增加了一倍。如果您需要一个不处理播放器就绪状态的紧凑函数,请参见http://jsfiddle.net/8R5y6/。

/**
 * @author       Rob W <gwnRob@gmail.com>
 * @website      https://*.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);
    }
}

Usage:

用法:

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");

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.

问:它不工作!A:“不工作”不是一个清晰的描述。你收到错误消息了吗?请出示相关代码。

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.

问:我已经嵌入了一个YouTube视频,使用

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.

问:我得到错误消息“指定无效或非法字符串”。为什么?答:API在本地主机上不能正常工作(文件://)。把你的(测试)页面放在网上,或者使用JSFiddle。例子:看看这个答案顶部的链接。

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.

问:你是怎么知道的?答:我花了一些时间来手动解释API的源代码。我的结论是,我必须使用postMessage方法。为了知道要传递哪些参数,我创建了一个Chrome扩展,它拦截消息。扩展的源代码可以在这里下载。

Q: What browsers are supported?
A: Every browser which supports JSON and postMessage.

问:支持哪些浏览器?A:每个支持JSON和postMessage的浏览器。

  • IE 8+
  • IE 8 +
  • Firefox 3.6+ (actually 3.5, but document.readyState was implemented in 3.6)
  • Firefox 3.6+(实际上是3.5,但是文档。readyState在3.6中实现
  • Opera 10.50+
  • Opera 10.50 +
  • Safari 4+
  • Safari 4 +
  • Chrome 3+
  • Chrome 3 +

Related answer / implementation: Fade-in a framed video using jQuery
Full API support: Listening for Youtube Event in jQuery
Official API: https://developers.google.com/youtube/iframe_api_reference

相关的答案/实现:使用jQuery的完整API支持在框架视频中使用:在jQuery官方API中监听Youtube事件:https://developers.google.com/youtube/iframe_api_reference。

Revision history

  • 17 may 2012
    Implemented onYouTubePlayerReady: callPlayer('frame_id', function() { ... }).
    Functions are automatically queued when the player is not ready yet.
  • 2012年5月17日执行onYouTubePlayerReady: callPlayer('frame_id', function(){…})。当玩家还没有准备好时,函数会自动排队。
  • 24 july 2012
    Updated and successully tested in the supported browsers (look ahead).
  • 2012年7月24日在受支持的浏览器中进行了更新和成功测试(展望未来)。
  • 10 october 2013 When a function is passed as an argument, callPlayer forces a check of readiness. This is needed, because when callPlayer 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 of postMessage, which was ignored.
  • 2013年10月10日,当一个函数作为参数传递时,callPlayer强制检查是否准备就绪。这是需要的,因为当callPlayer在iframe插入的时候被调用,而文档已经准备好了,它不能确定iframe已经完全准备好了。在Internet Explorer和Firefox中,这一场景导致了对postMessage的过早调用,这被忽略了。
  • 12 Dec 2013, recommended to add &origin=* in the URL.
  • 2013年12月12日,建议在URL中添加&origin=*。
  • 2 Mar 2014, retracted recommendation to remove &origin=* to the URL.
  • 2014年3月2日,撤回建议删除&来源=*到URL。

#2


29  

Looks like YouTube has updated their JS API so this is available by default! You can use an existing YouTube iframe's ID...

看起来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>

...in your JS...

…JS中……

var player;
function onYouTubeIframeAPIReady() {
  player = new YT.Player('player', {
    events: {
      'onStateChange': onPlayerStateChange
    }
  });
}

function onPlayerStateChange() {
  //...
}

...and the constructor will use your existing iframe instead of replacing it with a new one. This also means you don't have to specify the videoId to the constructor.

…构造函数将使用您现有的iframe,而不是用新的iframe替换它。这也意味着您不必为构造函数指定视频。

See Loading a video player

请参见加载视频播放器。

#3


13  

You can do this with far less code:

你可以用更少的代码来做到这一点:

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 || []
            }), '*');
        }
    }
}

Working example: http://jsfiddle.net/kmturley/g6P5H/296/

工作示例:http://jsfiddle.net/kmturley/g6P5H/296/

#4


3  

My own version of Kim T's code above which combines with some jQuery and allows for targeting of specific iframes.

我自己版本的Kim T的代码结合了一些jQuery并允许针对特定的iframe。

$(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 || []
        } ), '*');
    }
}