Question

I've created a method, i.e. connectEvent() inside an object called FL and it has properties including event, el and fn. When I use the method and then try to access this.el, the console.log() says undefined even though I have assigned window to it el.

Why is this.el undefined? Isn't this supposed to refer to the object FL?

JS:

var FL = {
    connectEvent : function(params) {

        var params = params || {};

        if (params.el.addEventListener) {
            params.el.addEventListener(params.event, params.fn, false);
        } else if (params.el.attachEvent) {
            params.el.attachEvent('on' + params.event, params.fn);
        } else {
            params.el['on' + params.event] = params.fn;
        }
    }
};

FL.connectEvent({ 
    event: 'resize', 
    el: window, 
    fn: function() {
        console.log('start on resize');

        console.log('wat is this.el? ', this.el);

        console.log('end on resize');
    } 
});
Was it helpful?

Solution

This is because in event listeners (and commonly in most callbacks) the function's "this", changes. In event listeners it's usually the element that triggers the event, in your case, this is actually your "el". To work around this you can do:

var FL = {
    connectEvent : function(params) {

        var params = params || {};
        var listener = function () {
            params.fn();
        };

        if (params.el.addEventListener) {
            params.el.addEventListener(params.event, listener, false);
        } else if (params.el.attachEvent) {
            params.el.attachEvent('on' + params.event, listener);
        } else {
            params.el['on' + params.event] = listener;
        }
    }
};

FL.connectEvent({ 
    event: 'resize', 
    el: window, 
    fn: function() {
        console.log('start on resize');

        console.log('wat is this.el? ', this.el);

        console.log('end on resize');
    } 
});

That way the listener function inherits the context it's created in, so it has access to params and calling params.fn() would preserve it's "this". If you want to also get the arguments of the event callback you can do:

var listener = function () {
     params.fn.apply(params, arguments);
};

The last one is equivalent to using:

var listener = params.fn.bind(params);

, but also works in older browsers.

OTHER TIPS

Two problems, First your never assigning to this in that function. So this will have no el property. Instead your assigning to a local variable params which is closed off to anyone outside that function. (Your also redefining it with the var statement. Best to remove it since you've already have it defined in the parameters of the function).

Second, You attempting to use this in a function which gets called from an event listener. When an event listener calls a callback function the context is set to something else (Depends on implementation / browser) instead of the original instance (this is the most common case). There for this references something else.

One approach would be to make connectEvent a proper constructor and use the new keyword and drop the use of params in favor of instance properties (this).

However, in your case it looks more likely to just force the context of the callback using bind()

var context = {
  el: params.el
};
params.el.addEventListener(params.event, params.fn.bind(context), false);

params.el.attachEvent('on' + params.event, params.fn.bind(context));

That way when the callback function does get called this will refer to the new object created called context.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top