Pass object variables into eventlistener - javascript

While trying to create a mouse listener to canvas object I faced a problem which took me a long time to solve - How can I pass object variables (this.X, this.Y) to an event listener, for example:
function Test() {
this.canvas = ....
this.mouseDownHandler = ....
canvas.addEventLIstener('mousedown', this.mouseDownListener, false);
}
So I came up with the following solution

This is the solution the worked for me -
function Test() {
this.ctx = this.canvas.getContext("2d");
var self = this;
this.canvas.addEventListener("mousedown",
function(e, param) {
self.mouseDownHandler(e, param);
}.bind(null, this), false);
}
Test.prototype.mouseDownHandler = function(t, e) {
t.ctx.fillRect(e.pageX, e.pageY, 10, 10);
};

If you don't mind me slightly simplifying #Yehonatan 's answer:
class Test {
constructor() {
this.canvas = document.getElementById("app")
this.ctx = this.canvas.getContext("2d")
this.canvas.addEventListener("mousedown", e => this.mouseDownHandler(e))
}
mouseDownHandler(e) {
this.ctx.fillRect(e.pageX, e.pageY, 10, 10)
}
}
let t = new Test()
JSFiddle example

Related

Losing object "this" context in a method [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I am doing some excercise, and I have issue with losing "this" context in a pipe method:
function Main() {
// something
this.render = function () {
this.groups.forEach(function(g){
renderGroup(g);
});
return this;
};
// something
this.pipe = function () {
this.render(); // 1
requestAnimationFrame(this.pipe); // 2
return this;
};
// something
}
Ad.1: that cause "this is undefined, so it has no render function"
Ad.2: if above commented, still "this" context is undefined so pipe is not a function
initialization:
function init () {
var m = new Main();
requestAnimationFrame(m.pipe);
}
window.onload = function () {
init();
}
full object code:
function Main() {
this.canvas = document.createElement("canvas");
this.canvas.width = 1366;
this.canvas.height = 768;
this.canvas.style.width = this.canvas.width + "px";
this.canvas.style.height = this.canvas.height + "px";
this.groups = [];
window.app = {};
this.grain = 30 * 1000 * 60;
this.grainWidth = 30;
this.getGroups = function () {return this.groups;}
this.render = function () {
this.groups.forEach(function(g){
renderGroup(g);
});
return this;
};
this.ctx = this.canvas.getContext("2d");
this.pipe = function () {
this.render();
requestAnimationFrame(this.pipe);
return this;
};
document.body.appendChild(this.canvas);
registerEvents();
}
the renderGroup is plain forEach.
What causes the context lost?
Simply bind the context you want pipe to be called with
this.pipe = function () {
this.render(); // 1
requestAnimationFrame(this.pipe); // 2
return this;
}.bind(this)
Maybe something like this?
requestAnimationFrame(this.pipe.bind(this));
JavaScript functions get this defined when they're called. You use pipe as callback, so the context for it becomes window since it's called from window.requestAnimationFrame.

Cannot access variable in object from outside

I didn't succeed in accessing the variable playing from outside the object Pong:
Pong = {
// some other objects
initialize: function (runner, cfg) {
Game.loadImages(Pong.Images, function (images) {
this.cfg = cfg;
this.runner = runner;
this.width = runner.width;
this.height = runner.height;
this.images = images;
this.playing = false; // variable is defined here
this.scores = [0, 0];
this.menu = Object.construct(Pong.Menu, this);
this.court = Object.construct(Pong.Court, this);
this.leftPaddle = Object.construct(Pong.Paddle, this);
this.rightPaddle = Object.construct(Pong.Paddle, this, true);
this.ball = Object.construct(Pong.Ball, this);
this.sounds = Object.construct(Pong.Sounds, this);
this.runner.start();
} .bind(this));
},
// some more functions
isPlaying: function () { // I added this function to enable for access
return this.playing; // here playing is undefined
},
start: function (numPlayers) {
if (!this.playing) { // here playing is defined
this.scores = [0, 0];
this.playing = true;
this.leftPaddle.setAuto(numPlayers < 1, this.level(0));
this.rightPaddle.setAuto(numPlayers < 2, this.level(1));
this.ball.reset();
this.runner.hideCursor();
}
},
// more objects and functions
This is a pingpong game. The complete page is this:
http://ulrichbangert.de/div/webdesign/javascript/pong.html I cannot understand why this variable can be accessed in start and not in isPlaying.
Why is this and what code do I have to use to access this variable? To enable for debugging I added calling isPlaying in the onclick event.
This is one of the classic problems with javascript, this changing "unexpectedly".
Inside of that callback, this points to something else, not your object. One of solutions is to trap object reference in a closure.
initialize: function (runner, cfg) {
var that = this; // <= "that" won't change.
Game.loadImages(Pong.Images, function (images) {
that.cfg = cfg;
that.playing = false;

Cannot reference method on Javascript object

I'm writing a version of Pong in Javascript. I have a Game object and I'm using the prototype property to define methods on it. I'm getting the following error: "Undefined is not a function". It is being thrown in the Game.prototype.step function so this.update is undefined in there. Here's the code for the Game object:
(function(root) {
var Pong = root.Pong = (root.Pong || {});
var Game = Pong.Game = function() {
this.canvas = document.getElementById('canvas');
this.canvas.width = 800;
this.canvas.height = 400;
this.context = canvas.getContext('2d');
this.maxStartSpeed = 10;
this.keysDown = {};
this.player2 = new Pong.Player({'player': 2});
this.player1 = new Pong.Player({'player': 1});
this.ball = new Pong.Ball(400, 200);
}
Game.prototype.update = function() {
this.player1.update();
this.player2.update();
this.ball.update(player1.paddle, player2.paddle);
};
Game.prototype.render = function() {
this.context.fillStyle = "#bdc3c7";
this.context.fillRect(0, 0, width, height);
this.player1.render();
this.player2.render();
this.ball.render();
};
Game.prototype.animate = function(callback) {
window.setTimeout(callback, 1000/60)
};
Game.prototype.step = function() {
this.update();
this.animate(this.step);
};
window.addEventListener("keydown", function (event) {
Game.keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function (event) {
delete Game.keysDown[event.keyCode];
});
window.onload = function() {
document.getElementById('canvas-container').appendChild(canvas);
game = new Game();
game.animate(game.step);
};
})(this);
The setTimeout is going to change the scope. To maintain the proper scope, you need to use bind
Change
this.animate(this.step);
to
this.animate(this.step.bind(this));
You need to do the same thing with the other animate calls.

Cant' change an object variable from an outer event

I was playing around with making game in JS. And hit a brick wall which is inability to change a variable from an event in the main html file. Namely speaking offSetX. Why it doesn't change?
var game = new Game();
window.addEventListener("keyup", game.input);
game.start('myCanvas');
The game object looks like this:
function Game() {
this.offSetX = 0;
this.init = function (id) {
this.canvas = document.getElementById(id);
this.context = this.canvas.getContext('2d');
this.blocks = [];
this.blocks.push(new block());
};
this.logic = function () {
for (var i in this.blocks) {
this.blocks[i].update(this.offSetX);
}
};
this.draw = function () {
for (var i in this.blocks) {
this.blocks[i].draw(this.context);
}
};
this.main = function () {
this.logic();
this.draw();
console.log(this.offSetX);
};
this.input = function (key) {
if (key.keyCode == 37) {
this.offSetX--;
console.log(this.offSetX);
}
if (key.keyCode == 39) {
this.offSetX++;
console.log(this.offSetX);
}
};
this.start = function (id) {
var _this = this;
this.init(id);
this.interval = setInterval(function () {
_this.canvas.width = _this.canvas.width;
_this.main();
}, 30);
}
};
Try this:
window.addEventListener("keyup", function(key){
game.input.apply(game,[key]);
});
The problem was by window.addEventListener("keyup", game.input) line , you are adding handler for window object, that's why in input method , "this" is window object(which does not have any "offSetX" method), not the game object.

How to access the outer this from jQuery functions?

out of curiosity is there a way to access this.color from the paint function?
function Foo(color)
{
this.color = color;
this.paint = function paint()
{
$("select").each(function(idx, el)
{
$(el).css("background", color); // OK
// $(el).css("background", this.color); // this.color is undefined
})
}
}
new Foo("red").paint();
Thanks
var that = this;
function (idx, el) {
// access what used to be this.color as that.color
}

Categories

Resources