Using Youtube's javascript API with jQuery

33,983

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();
}
Share:
33,983
Steerpike
Author by

Steerpike

Globetrotting Australian currently in Sydney Manager of product teams Web developer Interactive architect

Updated on October 08, 2020

Comments

  • Steerpike
    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 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.

  • Greg
    Greg about 15 years
    I've tested your code as-posted and onYouTubePlayerReady is never called
  • Steerpike
    Steerpike about 15 years
    You 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
    Greg about 15 years
    Ah 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
    Steerpike about 15 years
    Ok, 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
    Steerpike about 15 years
    You'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
    idrumgood almost 14 years
    Just wanted to say THANK YOU! This answer just saved my life.
  • Mustafa
    Mustafa almost 13 years
    "onYouTubePlayerReady must be outside of $(document).ready(function() to get fired" -- you saved my life
  • Leon Fedotov
    Leon Fedotov over 12 years
    see my answer - i has ze colure! ^_^
  • Volomike
    Volomike over 12 years
    This was not very clear to me. Where do I get a mediaController "class"?
  • JoshNaro
    JoshNaro over 12 years
    @Volomike it's just a self-defined JavaScript object used to help control the YouTube element.
  • TimKlimowicz
    TimKlimowicz about 12 years
    One 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
    Salman A over 11 years
    player.addEventListener accepts a string, not a function so you cannot use closures. It also rejects strings that contain certain characters so you cannot use expressions.