我不知道为什么没有删除事件监听器,虽然我准确地指出它们(我想是这样吗?)。
背景:这里是整个项目的链接:https://github.com/webmarek/jsninja
它基于JAVASCRIPT:DARREN JONES对NINJA的NOVICE
主js文件的代码(只需向下滚动一下并查看TLDR):
(function () {
"use strict"
//// dom references ////
var $question = document.getElementById("question");
var $score = document.getElementById("score");
var $feedback = document.getElementById("feedback");
var $start = document.getElementById("start");
var $form = document.getElementById("answer");
var $timer = document.getElementById("timer");
var quiz = {
"name": "Super Hero Name Quiz",
"description": "How many super heroes can you name?",
"question": "What is the real name of ",
"questions": [
{"question": "Superman", "answer": "Clarke Kent", "asked": false},
{"question": "Batman", "answer": "Bruce Wayne", "asked": false},
{"question": "Wonder Woman", "answer": "Dianna Prince", "asked": false},
{"question": "Spider Man", "answer": "Peter Parker", "asked": false},
{"question": "Iron Man", "answer": "Tony Stark", "asked": false},
]
};
var question; // current question
var initialScore = 0; // initialize score
var i = 0; //initialize counter of questions... maybe we can make it so it isn't global variable
/// view functions ///
function update(element, content, klass) {
console.trace();
var p = element.firstChild || document.createElement("p"); //firstChild works, because every element in HTM file has been created without any whitespaces inside
p.textContent = content;
element.appendChild(p);
if (klass) {
p.className = klass;
}
}
//helper functions
function hide(element) {
element.style.display = "none";
}
function show(element) {
element.style.display = "block";
}
// hide the form at the start of the game
hide($form);
//hide the timer
hide($timer);
//random function
function random(a, b, callback) {
if (b === undefined) {
// if only one argument is supplied, assume the lower limit is 1
b = a, a = 1;
}
var result = Math.floor((b - a + 1) * Math.random()) + a;
if (typeof callback === "function") {
result = callback(result);
}
return result;
}
//play function
function Game(quiz) {
//first lets set some properties
this.questions = quiz.questions;
this.phrase = quiz.question;
this.score = 0;//initialize score
update($score, this.score); //and make it visible
// initialize timer and set up an interval that counts down
this.time = 20;
update($timer, this.time);
this.interval = window.setInterval(this.countDown.bind(this), 1000);
// hide button, and show form and feedback and timer, besides, feedback needs to be cleared.
hide($start);
show($form);
show($timer);
$feedback.innerHTML = "";
show($feedback);
// add event listener to form for when it's submitted
$form.addEventListener('click', this.onClickFireCheck.bind(this), false);
this.chooseQuestion();
}
//here we fumble in the Game.prototype
Game.prototype.chooseQuestion = function () {
console.log("chooseQuestion() invoked");
var questions = this.questions.filter(function (question) {
return question.asked === false;
});
// set the current question
this.question = questions[random(questions.length) - 1];
this.ask(this.question);
}
Game.prototype.ask = function (question) {
console.log("ask() invoked");
var quiz = this;
// set the question.asked property to true so it's not asked again
question.asked = true;
update($question, this.phrase + question.question + '?');
//NOW CLEAR THE FUCKING FORM, YO...
$form.innerHTML = "";
// create an array to put the different options in and a button variable
var options = [], button;
var option1 = chooseOption();
options.push(option1.answer);
var option2 = chooseOption();
options.push(option2.answer);
// add the actual answer at a random place in the options array
options.splice(random(0, 2), 0, question.answer);
// loop through each option and display it as a button
options.forEach(function (name) {
button = document.createElement("button");
button.value = name;
button.textContent = name;
$form.appendChild(button);
});
// choose an option from all the possible answers but without choosing the same option twice. We have to choose one of the objects randomly, to find proposition for answer that lays within.
function chooseOption() {
var option = quiz.questions[random(quiz.questions.length) - 1];
// check to see if the option chosen is the current question or already one of the options, if it is then recursively call this function until it isn't
if (option === question || options.indexOf(option.answer) !== -1) {
return chooseOption();
}
return option;
}
}
Game.prototype.check = function (answer) {
console.log("check() invoked");
//ternaries
answer === this.question.answer ? (
update($feedback, "Correct!", "right"),
// increase score by 1
this.score++,
update($score, this.score))
: (
update($feedback, "Wrong!", "wrong")
)
i++;
//now how the hell do I check if thats last answer?
(i === quiz.questions.length) ? (
this.gameOver()
) : (
this.chooseQuestion()
)
}
Game.prototype.countDown = function () {
// decrease time by 1
this.time--;
// update the time displayed
update($timer, this.time);
// the game is over if the timer has reached 0
if (this.time <= 0) {
this.gameOver();
}
}
Game.prototype.onClickFireCheck = function (e) {
this.check(e.target.value);
}
Game.prototype.gameOver = function(){
console.log("gameOver() invoked");
// inform the player that the game has finished and tell them how many points they have scored
update($question, "Game Over, you scored " + this.score + " points");
hide($form);
hide($timer);
show($start);
// stop the countdown interval
window.clearInterval(this.interval);
//reset questions counter
i = 0;
this.questions.forEach(function (question) {
question.asked = false;
});
//remove event listener from $form
$form.removeEventListener("click", this.onClickFireCheck.bind(this), false);
}
// Event listeners
$start.addEventListener('click', function () {
new Game(quiz);
}, false);
update($score, initialScore);
//stop the automatic refresh of page when the form is submitted.
$form.addEventListener("submit", function (e) {
e.preventDefault();
}, false);
}());
现在TL; DR: 当我第一次玩这个游戏时,它运行正常。问了5个问题。当我再次玩游戏时,它不会问所有应有的问题(它确实像3而不是5)并且游戏结束。
再玩一次,它会问1或2个问题然后就可以了。
提示:
我检查了控制台,看起来每次玩游戏时,都会向$form
添加一个事件监听器(收听“点击”)。
在代码的底部,在gameOver方法中我将其删除。然而,经过3次演奏,其中有三个是事件听众。
我从传递给.bind(this)
的第二个参数中删除了removeEventListener
,但没有帮助。
答案 0 :(得分:0)
.bind()
会创建一个新的,唯一的函数对象,因此您传递给addEventListener
的函数与您传递给removeEventListener
的函数具有不同的身份。
如果要取消绑定处理程序,则必须传递相同的函数对象。
由于您的目标是将this
设置为Game
实例,因此您可以采用不同的方法。相反,向handleEvent
添加Game.prototype
方法,点击该方法后,会调用所需的方法。
Game.prototype.handleEvent = function(event) {
if (event.type === "click") {
this.onClickFireCheck(event);
}
};
然后add/remove
实例本身而不是方法。
$form.addEventListener('click', this, false);
// ...
$form.removeEventListener('click', this, false);
现在,当事件发生时,它将调用新的handleEvent
方法,该方法将为Game
值设置this
实例。这使您能够直接与绑定的实例进行交互,还可以在删除侦听器时传递相同的对象。