user1555863
user1555863

Reputation: 2607

why is the context of a handler the window object

Consider the following code:

var myObject = {
  items : ... //some DOM elements
  ,attachHandlers: function(){
    var self = this;
    self.items.forEach(function(){
      this.addEventListener("mouseover",self.mouseOverHandler,false);
    });
  }
  ,mouseOverHandler: function(event){
      console.log(this);
  }
};

window.onload = function()
{
  myObject.attachHandlers();
}

Running this on chrome, When mouseOverHandler is called, it prints:

Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

I wonder, is addEventListener changing my context to the window object? Why so? I want the event handler added to each element I'm iterating over.

Upvotes: 0

Views: 39

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075367

The reason relates to this line:

this.addEventListener("mouseover",self.mouseOverHandler,false);

In that line, this is the global object (which is available as window on browsers). That's the nature of how forEach works: Unless you specify a thisArg as the second argument to forEach (after the function), it uses undefined. In loose mode, that means that during the call to the iteration function, this is the global object. (In strict mode, it would mean this was undefined.)

If you meant to attach it to each element in the loop, then:

// Accept the arg here ------v
self.items.forEach(function (element) {
    element.addEventListener("mouseover", self.mouseOverHandler, false);
//  ^--- use it here
});

Then, this within mouseOverHandler would refer to the element, because that's a guarantee that addEventListener gives you. It would not refer to myObject. If you wanted it to refer to myObject object instead (although there's no reason to in this case), you might use Function#bind:

,attachHandlers: function(){
  var boundHandler = this.mouseOverHandler.bind(this);
  this.items.forEach(function(element){
    element.addEventListener("mouseover",boundHandler,false);
  });
}

Now, within mouseOverHandler, this would refer to your myObject object (although again, in this case, probably no reason to do that).

More (on my blog):


Side note: In your quoted code, you had addEventListner (missing e) rather than addEventListener, I've corrected it in the examples above.

Upvotes: 1

adeneo
adeneo

Reputation: 318332

It's not jQuery, this inside Array.forEach is not what you think it is, it's the global Window, while the first argument would be the currently iterated element

self.items.forEach(function (elem) {
    elem.addEventListener("mouseover", self.mouseOverHandler, false);
});

FIDDLE

Also, you can pass in this directly as an argument to forEach and avoid the variable like this

attachHandlers: function () {
    this.items.forEach(function (elem) {
        elem.addEventListener("mouseover", this.mouseOverHandler, false);
    }, this);
},

FIDDLE

Upvotes: 1

Related Questions