Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"this" doesn't work when binding functions to events inside a Javascript class

First off, I know I can copy "this" on instantiation, but that doesn't work here.

Basically I'm writing something to track people interacting with Youtube videos.

I got this working fine for one video at a time. But I want it to work on pages with multiple Youtube videos as well, so I converted the code to a class so I can create a new instance of it for each video on the page.

The problem is when trying to bind to the Youtube event listener for state changes. For "non-class" code, it looks like this:

var o = document.getElementById( id );
o.addEventListener("onStateChange", "onPlayerStateChange" );

(onPlayerStateChange being the function I wrote to track state changes in the video)

(I'm also aware that addEventListener won't work with MSIE but I'm not worrying about that yet)

But when I'm inside a class, I have to use "this" to refer to another function in that class. Here's what the code looks like:

this.o = document.getElementById( id );
this.o.addEventListener("onStateChange", "this.onPlayerStateChange" );

When it's written like this, this.onPlayerStateChange is never called. I've tried copying "this" into another variable, e.g. "me", but that doesn't work either. The onPlayerStateChange function is defined within the "this" scope before I do this:

var me = this;
this.o = document.getElementById( id );
this.o.addEventListener("onStateChange", "me.onPlayerStateChange" );

Any insights?

Looking through other similar questions here, all of them are using jQuery, and I think doing it that way might work if I did it that way. But I don't want to use jQuery, because this is going to be deployed on random third party sites. I love jQuery but I don't want it to be a requirement to use this.

like image 621
Sean Avatar asked Mar 24 '26 10:03

Sean


1 Answers

You need a global way to access the onPlayerStateChange method of your object. When you assign me as var me = this;, the variable me is only valid inside the object method where it is created. However, the Youtube player API requires a function that is accessible globally, since the actual call is coming from Flash and it has no direct reference to your JavaScript function.

I found a very helpful blog post by James Coglan in which he discussed a nice way to communicate with the Youtube's JavaScript API and manage events for multiple videos.

I have released a JavaScript wrapper library using his ideas at http://github.com/AnuragMishra/YoutubePlayer. Feel free to checkout the code. The underlying idea is simple - store all instances of the player object on the constructor. For example:

function Player(id) {
    // id of the placeholder div that gets replaced
    // the <object> element in which the flash video resides will
    // replace the placeholder div and take over its id
    this.id = id;

    Player.instances.push(this);
}

Player.instances = [];

When passing a string as a callback, use a string of the form:

"Player.dispatchEvent('playerId')"

When the flash player evals this string, it should return a function. That function is the callback that will ultimately receive the playback event id.

Player.dispatchEvent = function(id) {
    var player = ..; // search player object using id in "instances"
    return function(eventId) { // this is the callback that Flash talks to
        player.notify(eventId);
    };
};

When the flash player has loaded the video, the global onYoutubePlayerReady function is called. Inside that method, setup the event handlers for listening to playback events.

function onYouTubePlayerReady(id) {
    var player = ..; // find player in "instances"

    // replace <id> with player.id
    var callback = "YoutubePlayer.dispatchEvent({id})";
    callback = callback.replace("{id}", player.id);

    player.addEventListener('onStateChange', callback);
}

See a working example here..

like image 119
Anurag Avatar answered Mar 25 '26 23:03

Anurag



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!