pldg
pldg

Reputation: 2588

this keyword inside addEventListener callback

I read some other answer about this topic but I'm not sure I understand how this keyword works inside addEventListener.

const button = document.querySelector('button');

function foo() { console.log(this) }

button.addEventListener('click', foo);

foo is a regular function inside addEventListener, it's not a method on button object. When foo is called should be executed in the context of the global object, therefore this should be equal to window and not to button.

Looks like a situation similar to this example:

const obj = {
  method: function (cb) {
    console.log('method', this); // `this` === `obj`

    return cb();
  }
};

obj.method(function() {
  console.log('cb', this); // `this` === `window`
});

Where obj could be considered as button, method could be addEventListener and cb the callback inside addEventListener.

I know I can use bind to change the context of this but I want to understand more in depth why it works like that.

Why this inside addEventListener callback is invoked on the context of the current element instead of the global object?

Upvotes: 19

Views: 13051

Answers (5)

Aravindh S N R
Aravindh S N R

Reputation: 46

While we know that event listeners are executed with 'this' set to the event target, the below lines of code inside the EventTarget.prototype.dispatchEvent method in the EventTarget link that you found will answer your question as to how it is implemented.

for (var i = 0, l = stack.length; i < l; i++) {
    stack[i].call(this, event);
}

The 'stack' array has the callback functions and they are invoked using .call by passing in the event target instance (this) and event as arguments.

Upvotes: 3

Rajat Badjatya
Rajat Badjatya

Reputation: 818

As event handler is a type of callback, they are passed as a parameter to the function. Let's create a simple function and passed one callback as a parameter to it and see how it actually works.

    function testCallBack(fn){
       console.log('inside testCallBack');
       fn('Hello I am a callBack')
    }

    testCallBack(foo);

    function foo(param){
      console.log(param);
    }

// Outputs: 
inside testCallBack
Hello I am a callBack

Every scope in JavaScript has a this object that represents the calling object for the function.
That's the reason why this inside addEventListener callback is invoked on the context of the current element instead of the global object. Refer below code for more clear understanding:

   function sayNameForAll() {
        console.log(this.name);
    }

    var person1 = {
        name: "Rajat",
        sayName: sayNameForAll
    };

    var person2 = {
        name: "pldg",
        sayName: sayNameForAll
    };

    var name = "Sidd";

    person1.sayName();      // outputs "Rajat" here calling object is person1, so this represents person 1
    person2.sayName();      // outputs "pldg"

    sayNameForAll();        // outputs "Sidd"

So when you call button.addEventListner('click',foo), your calling object is button.

Upvotes: 2

Ben West
Ben West

Reputation: 4596

Just like you can use bind or call to set this to whatever you want, the browser APIs can also call your functions with any value set to this. It’s used in a bunch of weird ways and isn’t very consistent. Outside of classes and methods, this is more like a secret extra argument to a function. In this case you could avoid needing it by getting the button element from event.target.

Upvotes: 0

If we are using functions which have been defined using function keyword as an event handler, then that event handler function executes in the context of the element on which event was binded

button.addEventListener('click', foo);

so, in this case, this value inside foo will be button element.

If we use arrow functions instead of them then this value will be the window object

The reason is this in an arrow function has the same value as the context in which the arrow function was created

button.addEventListener('click', () => { console.log(this) // window } );

More about lexical this What is lexical 'this'?

Upvotes: 22

FallenWarrior
FallenWarrior

Reputation: 686

Event listeners are executed with this set to the object that triggered the event, as one listener can listen to events of many objects.

A regular function invocation however does not set this if the invocation expression does not contain a member access via .. In those cases, without "use strict" active, this will become the global context, which is window in the browser.

If you want this for cb to be obj, you could replace cb() with cb.apply(this), which would set cb's this to that of the enclosing function.

A final warning: these this mechanics only work for functions defined with the function keyword (and similar mechanics). The this inside of an arrow function becomes locked to that of the enclosing scope at the time of definition.

Upvotes: 2

Related Questions