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
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.
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:
froma.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 supportpostMessage
, 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
Document.Createelement("Script") Synchronously
How to Use Jquery for Xml Parsing with Namespaces
Defining Methods via Prototype VS Using This in the Constructor - Really a Performance Difference
Javascript: How to Join/Combine Two Arrays to Concatenate into One Array
Adding Click Event Listener to Elements with the Same Class
Variable as the Property Name in a JavaScript Object Literal
Why Don't We Just Use Element Ids as Identifiers in JavaScript
Over_Query_Limit in Google Maps API V3: How to Pause/Delay in JavaScript to Slow It Down
Is There Any Non-Eval Way to Create a Function with a Runtime-Determined Name
How to Deal with Big Numbers in JavaScript
What Is Minimum Millisecond Value of Settimeout
How to Convert an Array of Objects to Object with Key Value Pairs
Why Doesn't a JavaScript Return Statement Work When the Return Value Is on a New Line
Es6 Destructuring Function Parameter - Naming Root Object
How to Automate Shadow Dom Elements Using Selenium
What Do These JavaScript Bitwise Operators Do
Why Does Firebase Lose Reference Outside the Once() Function