Ruslan Plastun
Ruslan Plastun

Reputation: 2254

Can't use function in object using this

Below is my code when I add eventlistener in my object:

this.canvas.addEventListener('mousemove', function (e) {
    var canv = document.getElementById("some");
    myGameArea.x = (e.clientX - canv.getBoundingClientRect().left);//Look at canv
    myGameArea.y = (e.clientY - canv.getBoundingClientRect().top);//Look at canv
});

It works, but only when i use getElementById to find my canvas element and then use canv for my needs. But if i do this:

this.canvas.addEventListener('mousemove', function (e) {
    var canv = document.getElementById("some");
    myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
    myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Then myGameArea doesnt change at all and my debugger tells this: "Uncaught TypeError: Cannot read property 'getBoundingClientRect' of undefined at HTMLCanvasElement.". I have declared canvas in my object before this part of code and i used this.canvas for a lot of other functions. So where is my problem?

Upvotes: 0

Views: 114

Answers (3)

steliosbl
steliosbl

Reputation: 8921

Unlike normal variables, the value of the this keyword doesn't depend on where the function you're accessing it from is defined, but how it is called. In your case, this is being overwritten in the handler. A quick workaround would be the following:

var that = this;
this.canvas.addEventListener('mousemove', function (e) {
    myGameArea.x = (e.clientX - that.canvas.getBoundingClientRect().left);//Look at this.canvas
    myGameArea.y = (e.clientY - that.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Here we explicitly state the object we want to access, and thus don't need to use this at all. However, this is not considered best practice. The recommended way to deal with nested scope is to bind the function to the context you want:

this.canvas.addEventListener('mousemove', function (e) {
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
}.bind(this));

Or, if you're using ES6, use fat arrow notation (() => {}), which doesn't create its own context, for the handler:

this.canvas.addEventListener('mousemove', (e) => {
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Upvotes: 0

Joshua Jones
Joshua Jones

Reputation: 1396

Within the callback closure, the context of this is the canvas element itself. Therefore, you would just use this to reference the canvas.

this.canvas.addEventListener('mousemove', function (e) {
    myGameArea.x = (e.clientX - this.getBoundingClientRect().left);
    myGameArea.y = (e.clientY - this.getBoundingClientRect().top);
});

Upvotes: 2

hackerrdave
hackerrdave

Reputation: 6706

In the callback function of addEventListener, this is no longer the same value that it was outside of the callback. If you would like to bind to the same this value you can use either .bind(this) or use arrow func:

this.canvas.addEventListener('mousemove', function (e) {
  var canv = document.getElementById("some");
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
}.bind(this));

this.canvas.addEventListener('mousemove', (e) => {
  var canv = document.getElementById("some");
  myGameArea.x = (e.clientX - this.canvas.getBoundingClientRect().left);//Look at this.canvas
  myGameArea.y = (e.clientY - this.canvas.getBoundingClientRect().top);//Look at this.canvas
});

Upvotes: 0

Related Questions