Tutorial :Using Youtube's javascript API with jQuery



Question:

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 with getElementById(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 case playerState).

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.


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


Solution:4

Here's a nice article that goes through creating a class to wrap an individual player, including dispatching events to individual objects using a similar approach to that mentioned in a previous answer.

http://blog.jcoglan.com/2008/05/22/dispatching-youtube-api-events-to-individual-javascript-objects/


Solution:5

How about something like so:

var closureFaker = function (func, scope) {      var functionName = 'closureFake_' + (((1+Math.random())*0x10000000)|0).toString(16);      window[functionName] = function () {          func.apply(scope || window, arguments);      };      console.log('created function:', functionName, window[functionName]);      return functionName;  };    ytplayer.addEventListener("onStateChange", closureFaker(function () {      //place your logic here      console.log('state change', arguments)  }));  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »