Youtube Iframe API: How to Control an Iframe Player That's Already in the HTML

YouTube iframe API: how do I control an iframe player that's already in the HTML?

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

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.

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

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.

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

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.

Q: What browsers are supported?

A: Every browser which supports JSON and postMessage.

  • IE 8+
  • Firefox 3.6+ (actually 3.5, but document.readyState was implemented in 3.6)
  • Opera 10.50+
  • Safari 4+
  • 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

Revision history

  • 17 may 2012

    Implemented onYouTubePlayerReady: 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 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.
  • 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.

Loading the Youtube Player API inside an iframe

I'm sorry mate. The solution to your problem is "easy" to code/implement but painful and difficult to maintain.

Youtube API does not allow to embed a YT.Player object within an Iframe (e.g. div within an iframe), because it looks for the 'player' node within the window object and not in the iframe document.

So, a quick hotfix for this would be to save a copy of the API files and modify them to add this functionality. Obviously, from that moment on, it is your responsability to serve these files and also to update them in order the files do not get deprecated.

The solution would be (I take for granted JQuery is loaded before Youtube API):

Using the base example provided at https://developers.google.com/youtube/iframe_api_reference#Getting_Started I guess you have this
<iframe id="if"></iframe> instead of <div id="player"></div> and that later on you append the player div inside that iframe.

<body>
<iframe id="if"></iframe>

<script>
$('#if').contents().find('body').append($('<div id="player"></div>'));
// ...

So, whem defining onYoutubeAPIReady(), you must add 1 parameter to the YT.Player constructor:

function onYouTubeIframeAPIReady() {
player = new YT.Player('player', { /* options */ }, $('#if') );

That is $('#if'), the iframe element where you want to embed the player.

iframe_api

In this file you just need to modify the src of the script it loads:

from
a.src = 'http://s.ytimg.com/yts/jsbin/www-widgetapi-vflOb0oo1.js

to a.src = './widget.js'; (widget.js is your copy of www-widgetapi-vflOb0oo1.js).

Finally on widget.js:

Here you must modify this 2 functions: function S(a,b) and function Y(a,b).

First function Y(a,b) to function Y(a,b,c) in order to get the iframe parameter. Then in its body you change S.call(this,a,new nb(b)); to S.call(this,a,new nb(b),c);

Second, function S(a,b) to function S(a,b, dom) and c = document to
c= dom === undefined ? document : dom.contents()[0].

Now you have a Youtube player inside your iframe and you are able to use it from the parent window.

I hope it is useful! ;)

How to control iframe based YouTube player?

https://developers.google.com/youtube/iframe_api_reference#Operations

We support a similar set of functions
for the IFrame API as are currently
supported for the JavaScript API.

Please refer to that document for a
list of functions. Please note that
the functions that deal with video
bytes behave differently when HTML5
playback is used via the IFrame API.
getVideoBytesTotal is hardcoded to
return 1000. getVideoBytesLoaded will
return a value between 0 and 1000. To
calculate the fraction of the video
that has been loaded you can divide
the getVideoBytesLoaded value by the
getVideoBytesTotal value, and that
calculation will work regardless of
whether HTML5 or ActionScript 3
playback is used.

Do note it is a experimental service which should not be used for production level applications.

Notice

Important: This is an experimental
feature, which means that it might
change unexpectedly.

Access youtube API with exist Iframe (using share on the youtube video website)

You can visit this documentation:

Requirements

The user's browser must support the HTML5 postMessage feature.
Most modern browsers support postMessage, though Internet
Explorer 7 does not support it.

Embedded players must have a viewport that is at least 200px by 200px.
If the player displays controls, it must be large enough to fully
display the controls without shrinking the viewport below the minimum
size. We recommend 16:9 players be at least 480 pixels wide and 270
pixels tall.

Any web page that uses the IFrame API must also implement the
following JavaScript function:

  • onYouTubeIframeAPIReady – The API will call this function when the page has finished downloading the JavaScript for the player
    API, which enables you to then use the API on your page. Thus, this
    function might create the player objects that you want to display when
    the page loads.

Examples were also given to start with.

You can also take this SO post as a reference.

The JavaScript here works.

var player;
function onYouTubeIframeAPIReady() {
video = document.getElementById('video0');
player = new YT.Player(video, {
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
})
}
function onPlayerReady(e) {
console.log("hey Im ready");
}
function onPlayerStateChange(e) {
console.log(e)
}

How do I interact with the YouTube player on youtube.com – not the iFrame API?

The actual player object that can be interacted with has the ID movie_player. This ID may of course change over time. As of October 2016, the ID is the same for Chrome, Firefox, and Safari.

For example:

var p = document.getElementById("movie_player");
p.addEventListener("onPlaybackQualityChange", function() { console.log("quality changed") }, true);

Other event listeners can of course be added according to the iFrame API.

The player can also be interacted with as usual:

p.playVideo() 


Related Topics



Leave a reply



Submit