Using Youtube's javascript API with jQuery
Solution 1
Edit:
Apparently calling addEventListener
on the player
object causes the script to be used as a string in an XML property that's passed to the flash object - this rules out closures and the like, so it's time for an old-school ugly hack:
function onYouTubePlayerReady(playerId) {
var player = $('#'+playerId)[0];
player.addEventListener('onStateChange', '(function(state) { return playerState(state, "' + playerId + '"); })' );
}
function playerState(state, playerId) {
console.log(state);
console.log(playerId);
}
Tested & working!
Solution 2
Im Using Jquery SWFobject plugin, SWFobject
It is important to add &enablejsapi=1 at the end of video
HTML:
<div id="embedSWF"></div>
Jquery:
$('#embedSWF').flash({
swf: 'http://www.youtube.com/watch/v/siBoLc9vxac',
params: { allowScriptAccess: "always"},
flashvars: {enablejsapi: '1', autoplay: '0', allowScriptAccess: "always", id: 'ytPlayer' },
height: 450, width: 385 });
function onYouTubePlayerReady(playerId) {
$('#embedSWF').flash(function(){this.addEventListener("onStateChange", "onPlayerStateChange")});
}
function onPlayerStateChange(newState) {
alert(newState);
}
onYouTubePlayerReady must be outside of $(document).ready(function() to get fired
Solution 3
I had this same problem and tried the accepted answer. This didn't work for me; the playerState() function was never called. However, it put me on the right path. What I ended up doing was this:
// Within my mediaController "class"
window["dynamicYouTubeEventHandler" + playerID] = function(state) { onYouTubePlayerStateChange(state, playerID); }
embedElement.addEventListener("onStateChange", "dynamicYouTubeEventHandler" + playerID);
// End mediaController class
// Global YouTube event handler
function onYouTubePlayerStateChange(state, playerID) {
var mediaController = GetMediaControllerFromYouTubeEmbedID(playerID);
mediaController.OnYouTubePlayerStateChange(state);
}
It's fairly nasty, but so is the current state of the YouTube JavaScript API.
Here is some other helpful/nasty code if you are using any kind of advanced prototyping patterns. This basically allows you to retrieve a "class" instance from the YouTube player ID:
// Within my mediaController "class"
// The containerJQElement is the jQuery wrapped parent element of the player ID
// Its ID is the same as the player ID minus "YouTubeEmbed".
var _self = this;
containerJQElement.data('mediaController', _self);
// End mediaController class
// Global YouTube specific functions
function GetMediaControllerFromYouTubeEmbedID(embedID) {
var containerID = embedID.replace('YouTubeEmbed', '');
var containerJQObject = $("#" + containerID);
return containerJQObject.data('mediaController');
}
function onYouTubePlayerReady(playerId) {
var mediaController = GetMediaControllerFromYouTubeEmbedID(playerId);
mediaController.OnYouTubeAPIReady();
}
Steerpike
Globetrotting Australian currently in Sydney Manager of product teams Web developer Interactive architect
Updated on October 08, 2020Comments
-
Steerpike over 3 years
I'm currently trying to use the YouTube API as part of a jQuery plugin and I've run into a bit of a problem.
The way the YT api works is that you load the flash player and, when it's ready it will send a call back to a global function called
onYouTubePlayerReady(playerId)
. You can then use that id combined withgetElementById(playerId)
to send javascript calls into the flash player (ie,player.playVideo();
).You can attach an event listener to the player with
player.addEventListener('onStateChange', 'playerState');
which will send any state changes to another global function (in this caseplayerState
).The problem is I'm not sure how to associate a state change with a specific player. My jQuery plugin can happily attach more than one video to a selector and attach events to each one, but the moment a state actually changes I lose track of which player it happened in.
I'm hoping some example code may make things a little clearer. The below code should work fine in any html file.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="application/text+html;utf-8"/> <title>Sandbox</title> <link type="text/css" href="http://jqueryui.com/latest/themes/base/ui.all.css" rel="stylesheet" /> <script type="text/javascript" src="http://www.google.com/jsapi"></script> <script type="text/javascript"> google.load("jquery", "1.3.2"); google.load("jqueryui", "1.7.0"); </script> <script type="text/javascript" src="http://swfobject.googlecode.com/svn/tags/rc3/swfobject/src/swfobject.js"></script> <script type="text/javascript"> (function($) { $.fn.simplified = function() { return this.each(function(i) { var params = { allowScriptAccess: "always" }; var atts = { id: "ytplayer"+i }; $div = $('<div />').attr('id', "containerplayer"+i); swfobject.embedSWF("http://www.youtube.com/v/QTQfGd3G6dg&enablejsapi=1&playerapiid=ytplayer"+i, "containerplayer"+i, "425", "356", "8", null, null, params, atts); $(this).append($div); }); } })(jQuery); function onYouTubePlayerReady(playerId) { var player = $('#'+playerId)[0]; player.addEventListener('onStateChange', 'playerState'); } function playerState(state) { console.log(state); } $(document).ready(function() { $('.secondary').simplified(); }); </script> </head> <body> <div id="container"> <div class="secondary"> </div> <div class="secondary"> </div> <div class="secondary"> </div> <div class="secondary"> </div> </div> </body> </html>
You'll see the console.log() outputtin information on the state changes, but, like I said, I don't know how to tell which player it's associated with.
Anyone have any thoughts on a way around this?
EDIT: Sorry, I should also mentioned that I have tried wrapping the event call in a closure.
function onYouTubePlayerReady(playerId) { var player = $('#'+playerId)[0]; player.addEventListener('onStateChange', function(state) { return playerState(state, playerId, player); } ); } function playerState(state, playerId, player) { console.log(state); console.log(playerId); }
In this situation
playerState
never gets called. Which is extra frustrating. -
Greg about 15 yearsI've tested your code as-posted and onYouTubePlayerReady is never called
-
Steerpike about 15 yearsYou sure you got all the code? I just re-tested it and I get four 5's outputted to my console.log when the videos load. Are the videos actually appearing for you?
-
Greg about 15 yearsAh apparently it only works if you access it through a web server. It looks like addEventListener only is only accepting a string - I'm not sure why though...
-
Steerpike about 15 yearsOk, I thought I was going crazy. Nice to see someone else a bit confused as to how YT has implemented events. I think I may have to go back to the drawing board with this one. I was just hoping someone might have a stroke of genius.
-
Steerpike about 15 yearsYou're a legend! Sorry it took me so long to respond, I left work and just got home and tested your latest version. Brilliant stuff. Thank you.
-
idrumgood almost 14 yearsJust wanted to say THANK YOU! This answer just saved my life.
-
Mustafa almost 13 years"onYouTubePlayerReady must be outside of $(document).ready(function() to get fired" -- you saved my life
-
Leon Fedotov over 12 yearssee my answer - i has ze colure! ^_^
-
Volomike over 12 yearsThis was not very clear to me. Where do I get a mediaController "class"?
-
JoshNaro over 12 years@Volomike it's just a self-defined JavaScript object used to help control the YouTube element.
-
TimKlimowicz about 12 yearsOne small note: the onYouTubePlayerReady() function code can be within the $(document).ready, but it must be scoped to the window/global object outside of jQuery, so the function would instead need to be defined as such: window.onYouTubePlayerReady(playerId) {}
-
Salman A over 11 years
player.addEventListener
accepts astring
, not afunction
so you cannot use closures. It also rejects strings that contain certain characters so you cannot use expressions.