Reputation: 1926
I'm not sure what's wrong here, but testing in the chromium and firefox, I find that I'm doing it wrong with respect to removing an EventListener from an element in javascript.
The context is a canvas game. At first, there's a splash screen shown where you click to begin the game. After you click to begin, I want to remove the listener.
The main point of interest is the removeEventListener in the startGame function. It doesn't throw an error. And the code executes (I see the game starting message in the console and I can see that "this" is the Game instance). I'm totally confused why if I keep on clicking on the canvas runs startGame each time. The expected behavior is that clicking there does nothing once the EventListener is removed.
Help!
function Game(canvas) {
this.c = canvas;
this.ctx = this.c.getContext("2d");
this.c.width = CANVAS_WIDTH;
this.c.height = CANVAS_HEIGHT;
// Background image
this.bgReady = false;
this.bgImage = new Image();
this.bgImage.onload = function () {
window.g.bgReady = true;
};
this.bgImage.src = MAIN_BACKGROUND;
}
Game.prototype.setSplash = function() {
if (this.bgReady) {
this.ctx.drawImage(window.g.bgImage, 0, 0);
this.ctx.font="48px Helvetica";
this.ctx.textAlign = "center";
this.ctx.fillStyle="rgb(0,0,255)";
this.ctx.fillText("Click To Start",310,240);
document.getElementById("cnvs").addEventListener(
'click',this.startGame.bind(this),true);
} else {
// since setSplash is an early function
// wait a bit for the background image and then try again
setTimeout(this.setSplash.bind(this),100);
console.log("bgImage not ready...");
}
}
Game.prototype.startGame = function() {
console.log("game starting ...");
console.log(this);
// step 1, remove the click listener for this function
// why isn't this working?!
document.getElementById("cnvs").removeEventListener(
'click',this.startGame,true);
}
...
// other stuff ...
function initialize() {
// Get the canvas
var c = document.getElementById("cnvs");
// Create a game object
window.g = new Game(c);
// Set the splash page
g.setSplash();
}
window.onload=initialize;
Further info:
I also had a version where the non-working removal was written as:
this.c.removeEventListener('click',this.startGame,true);
Same behavior as the code referenced above.
EDIT: in reply to the first answer by mczepiel
I'm trying to implement your answer like this:
Typer.prototype.setSplash = function() {
if (this.bgReady) {
this.ctx.drawImage(window.t.bgImage, 0, 0);
this.ctx.font="48px Helvetica";
this.ctx.textAlign = "center";
this.ctx.fillStyle="rgb(0,0,255)";
this.ctx.fillText("Click To Start",310,240);
var boundFunction = this.startGame.bind(this);
document.getElementById("cnvs").addEventListener(
'click',boundFunction,true,boundFunction);
} else {
// since setSplash is an early function
// wait a bit for the background image and then try again
setTimeout(this.setSplash.bind(this),100);
console.log("bgImage not ready...");
}
}
Typer.prototype.startGame = function(boundFunction) {
console.log("game starting ...");
console.log(this); // strangely, now this is an Object rather
// than Game, it still has the properties of
// Game tho
// step 1, remove the click listener for this function
// still isn't working...
document.getElementById("cnvs").removeEventListener(
'click',boundFunction,true);
}
I think I understood your suggestion, but perhaps not. The code above still doesn't remove the listener. Any help appreciated.
Upvotes: 1
Views: 5389
Reputation: 711
You'll need to store a reference to the result of calling this.startGame.bind(this)
and pass that same value to both addEventListener
and removeEventListener
The remove call is expecting to remove the exact same object that was added as a listener.
Likely duplicate of removeEventListener is not working and others if you want to see the same issue in various flavors.
EDIT untested off-the-cuff suggestion:
Typer.prototype.setSplash = function() {
if (this.bgReady) {
// draw stuff
var canvasElement = document.getElementById("cnvs");
var dismissSplash = function (evt) {
canvasElement.removeEventListener('click', dismissSplash, true);
this.startGame();
}.bind(this);
canvasElement.addEventListener('click', dismissSplash, true);
} else {
// try to show splash later
}
}
Typer.prototype.startGame = function() {
// start game
}
Upvotes: 5