removeEventListener(),但是监听器仍在那里

时间:2017-10-31 15:06:28

标签: javascript events javascript-events event-handling

我不知道为什么没有删除事件监听器,虽然我准确地指出它们(我想是这样吗?)。

背景:这里是整个项目的链接: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添加一个事件监听器(收听“点击”)。

print console

在代码的底部,在gameOver方法中我将其删除。然而,经过3次演奏,其中有三个是事件听众。

我从传递给.bind(this)的第二个参数中删除了removeEventListener,但没有帮助。

1 个答案:

答案 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实例。这使您能够直接与绑定的实例进行交互,还可以在删除侦听器时传递相同的对象。