Listening for Youtube Event in JavaScript or jQuery

I have a slider which includes 4 youtube videos that are embedded via the iframe embed code'.$i.'?enablejsapi=1

I’m trying to make the onStateChange event of any of the four videos call a function I have called stopCycle() which will stop the slider when the video begins to play. The iframes do not have an id. I’m not sure about how to capture this event properly and could use any advice as to what i’m doing wrong.

<script charset="utf-8" type="text/javascript" src=""></script>

var playerObj = document.getElementById("tab2"); // the container for 1 of the 4 iframes

playerObj.addEventListener("onStateChange", "stopCycle");

function stopCycle(event) {

The YouTube Frame API does support existing frames. To improve the usage, I have created some helper functions. Have a look at the code + comments below and the demo:

To bind functions to existent frames, you have to pass an ID reference to the frame. In your case, the frame is contained within a container with id="tab2". I have defined a custom function for an easier implementation:

function getFrameID(id){
    var elem = document.getElementById(id);
    if (elem) {
        if(/^iframe$/i.test(elem.tagName)) return id; //Frame, OK
        // else: Look for frame
        var elems = elem.getElementsByTagName("iframe");
        if (!elems.length) return null; //No iframe found, FAILURE
        for (var i=0; i<elems.length; i++) {
           if (/^https?:\/\/(?:www\.)?youtube(?:-nocookie)?\.com(\/|$)/i.test(elems[i].src)) break;
        elem = elems[i]; //The only, or the best iFrame
        if ( return; //Existing ID, return it
        // else: Create a new ID
        do { //Keep postfixing `-frame` until the ID is unique
            id += "-frame";
        } while (document.getElementById(id)); = id;
        return id;
    // If no element, return null.
    return null;

// Define YT_ready function.
var YT_ready = (function() {
    var onReady_funcs = [], api_isReady = false;
    /* @param func function     Function to execute on ready
     * @param func Boolean      If true, all qeued functions are executed
     * @param b_before Boolean  If true, the func will added to the first
                                 position in the queue*/
    return function(func, b_before) {
        if (func === true) {
            api_isReady = true;
            while (onReady_funcs.length) {
                // Removes the first func from the array, and execute func
        } else if (typeof func == "function") {
            if (api_isReady) func();
            else onReady_funcs[b_before?"unshift":"push"](func); 
// This function will be called when the API is fully loaded
function onYouTubePlayerAPIReady() {YT_ready(true)}

// Load YouTube Frame API
(function() { // Closure, to not leak to the scope
  var s = document.createElement("script");
  s.src = (location.protocol == 'https:' ? 'https' : 'http') + "://";
  var before = document.getElementsByTagName("script")[0];
  before.parentNode.insertBefore(s, before);

// Previously, core functions were defined. Look ahead for the implementation:

var player; //Define a player object, to enable later function calls, without
            // having to create a new class instance again.

// Add function to execute when the API is ready
    var frameID = getFrameID("tabs2");
    if (frameID) { //If the frame exists
        player = new YT.Player(frameID, {
            events: {
                "onStateChange": stopCycle

// Example: function stopCycle, bound to onStateChange
function stopCycle(event) {
    alert("onStateChange has fired!\nNew state:" +;

If you want to invoke additional functions at a later point, e.g. mute a video, use:

  • If you only have to call simple single-direction functions, it’s not necessary to use this code. Instead, use the function callPlayer as defined at this answer.
  • If you want to implement this feature for multiple frames, simultaneously, have a look at this answer. Also includes a detailed explanation of getFrameID and YT_ready.

Since yesterday this isn’t working any more. The onStateChange isn’t fired.
Jeffrey posted an quick fix but I am not able to update the above answer

More info about this issue

Got it working 🙂 with the fix

Add the following function

function onReady() {
    player.addEventListener('onStateChange', function(e) {
        console.log('State is:',;

and before “onStateChange”: stopCycle add “onReady”: onReady,

I have this feature on my chat that enables users to post vids from youtube.
The url of the vid is attached as source of existing iframe with id.

What i notice is that -although ?enablejsapi=1 is added – that the script only works far existing iframes on pageload.

How would one go about when wanting this script to bind after a new source is set for the iframe?

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Similar Posts