简体   繁体   中英

How to stop Javascript from calling function multiple times when game is restarted

I am currently making a simple game called the simon game. It works pretty fine until the game is restarted. It calls on the function handler() multiple times. I tried to clear out the array with a function called startOver(). But I think the only problem is that it calls on the handler function multiple times. I don't understand why. Is there a way to clear out a function? or just a way to fix it? Here is my code:

let buttonColors = ["red", "blue", "green", "yellow"]
let gamePattern = []
let level = 1;
let userClickedPattern = []
let currentLevel = gamePattern.length
let started = false

document.addEventListener("keydown",function(event){
    if (!started){
        if (event.key=="a"){
            nextSequence();
            handler();
       
            started = true
        }
    }
})


function checker(){
    if(userClickedPattern[currentLevel]===gamePattern[currentLevel]){
        if(userClickedPattern.length===gamePattern.length){
            level++;
            userClickedPattern =[];
            setTimeout(nextSequence, 1000)
        }
    }
    else if(userClickedPattern[currentLevel]!=gamePattern[currentLevel]){
        document.querySelector("h1").innerText = "Game Over. Press 'B' key to start again";
        let error1 = new Audio("sounds/wrong.mp3");
        error1.play()
        document.querySelector("body").classList.add("game-over")
        setTimeout(function(){
            document.querySelector("body").classList.remove("game-over")
        }, 200)
        startOver();
    }
}

function nextSequence(){
    let randomChosenColor = buttonColors[Math.floor(Math.random()*4)];
    let sound1 = new Audio("sounds/"+randomChosenColor+".mp3");
    sound1.play()
    gamePattern.push(randomChosenColor)
    document.querySelector("h1").innerText = "Level "+(level);
    //STYLE
    document.querySelector("#"+randomChosenColor).classList.add("pressed");
    setTimeout(function (){document.querySelector("#"+randomChosenColor).classList.remove("pressed")
    }, 200)
  
}

function handler(){
    for(let i = 0; i<document.querySelectorAll(".btn").length; i++){
        document.querySelectorAll(".btn")[i].addEventListener("click", function(){
            let userChosenColor = this.getAttribute("id");
            userClickedPattern.push(userChosenColor)
            //SOUND
            let sound1 = new Audio("sounds/"+userChosenColor+".mp3");
            sound1.play()
            //STYLE
            document.querySelector("#"+userChosenColor).classList.add("pressed");
            setTimeout(function () 
                {document.querySelector("#"+userChosenColor).classList.remove("pressed")
            }, 200)
            checker();      
            console.log(gamePattern);
            console.log(userClickedPattern);     
        })      
    }
}


function startOver(){
    gamePattern = [];
    userClickedPattern = [];
    level = 1;    
    started = false

}

I know my code is pretty ugly, I'm still very new to coding.

Plot twist! handler isn't being called multiple times... you've registered "click" events on your .btn s but you're not removing the event listener in startOver , resulting in the click seeming to fire multiple times.

Here's how you fix it:

  1. Your click handler function cannot be an anonymous function because we'll need to reference it again later.
  2. You need to call Element.removeEventListener for each of your .btn elements from inside of startOver .

Instead of this:

function handler(){
    for(let i = 0; i<document.querySelectorAll(".btn").length; i++){
        document.querySelectorAll(".btn")[i].addEventListener("click", function(){
            let userChosenColor = this.getAttribute("id");
            userClickedPattern.push(userChosenColor)
            //SOUND
            let sound1 = new Audio("sounds/"+userChosenColor+".mp3");
            sound1.play()
            //STYLE
            document.querySelector("#"+userChosenColor).classList.add("pressed");
            setTimeout(function () 
                {document.querySelector("#"+userChosenColor).classList.remove("pressed")
            }, 200)
            checker();      
            console.log(gamePattern);
            console.log(userClickedPattern);     
        })      
    }
}

We'll write this:


// This was inside `addEventListener('click', [it was here])` before.
function handler() {
  let userChosenColor = this.getAttribute('id')
  userClickedPattern.push(userChosenColor)
  //SOUND
  let sound1 = new Audio('sounds/' + userChosenColor + '.mp3')
  sound1.play()
  //STYLE
  document.querySelector('#' + userChosenColor).classList.add('pressed')
  setTimeout(function () {
    document.querySelector('#' + userChosenColor).classList.remove('pressed')
  }, 200)
  checker()
  console.log(gamePattern)
  console.log(userClickedPattern)
}

function registerHandlers() {
  // This used to be inside of the `handler` function.
  for (let i = 0; i < document.querySelectorAll('.btn').length; i++) {
    document.querySelectorAll('.btn')[i].addEventListener('click', handler)
  }
}

And we'll change your "keydown" listener to use the new registerHandlers function instead of handler :

document.addEventListener('keydown', function (event) {
  if (!started) {
    if (event.key == 'a') {
      nextSequence()
      registerHandlers()

      started = true
    }
  }
})

And finally, we'll clean up the event listeners in startOver :

function startOver() {
  for (let i = 0; i < document.querySelectorAll('.btn').length; i++) {
    // Get rid of the old click listeners
    document.querySelectorAll('.btn')[i].removeEventListener('click', handler)
  }
  gamePattern = []
  userClickedPattern = []
  level = 1
  started = false
}

All together now!

Here's the complete code. You should be able to paste this in and have it work as expected. Other, yet-to-be found bugs, notwithstanding.

let buttonColors = ['red', 'blue', 'green', 'yellow']
let gamePattern = []
let level = 1
let userClickedPattern = []
let currentLevel = gamePattern.length
let started = false

document.addEventListener('keydown', function (event) {
  if (!started) {
    if (event.key == 'a') {
      nextSequence()
      registerHandlers()

      started = true
    }
  }
})

function checker() {
  if (userClickedPattern[currentLevel] === gamePattern[currentLevel]) {
    if (userClickedPattern.length === gamePattern.length) {
      level++
      userClickedPattern = []
      setTimeout(nextSequence, 1000)
    }
  } else if (userClickedPattern[currentLevel] != gamePattern[currentLevel]) {
    document.querySelector('h1').innerText =
      "Game Over. Press 'B' key to start again"
    let error1 = new Audio('sounds/wrong.mp3')
    error1.play()
    document.querySelector('body').classList.add('game-over')
    setTimeout(function () {
      document.querySelector('body').classList.remove('game-over')
    }, 200)
    startOver()
  }
}

function nextSequence() {
  let randomChosenColor = buttonColors[Math.floor(Math.random() * 4)]
  let sound1 = new Audio('sounds/' + randomChosenColor + '.mp3')
  sound1.play()
  gamePattern.push(randomChosenColor)
  document.querySelector('h1').innerText = 'Level ' + level
  //STYLE
  document.querySelector('#' + randomChosenColor).classList.add('pressed')
  setTimeout(function () {
    document.querySelector('#' + randomChosenColor).classList.remove('pressed')
  }, 200)
}

function registerHandlers() {
  for (let i = 0; i < document.querySelectorAll('.btn').length; i++) {
    document.querySelectorAll('.btn')[i].addEventListener('click', handler)
  }
}

function handler() {
  let userChosenColor = this.getAttribute('id')
  userClickedPattern.push(userChosenColor)
  //SOUND
  let sound1 = new Audio('sounds/' + userChosenColor + '.mp3')
  sound1.play()
  //STYLE
  document.querySelector('#' + userChosenColor).classList.add('pressed')
  setTimeout(function () {
    document.querySelector('#' + userChosenColor).classList.remove('pressed')
  }, 200)
  checker()
  console.log(gamePattern)
  console.log(userClickedPattern)
}

function startOver() {
  for (let i = 0; i < document.querySelectorAll('.btn').length; i++) {
    document.querySelectorAll('.btn')[i].removeEventListener('click', handler)
  }
  gamePattern = []
  userClickedPattern = []
  level = 1
  started = false
}

So my hunch was right that it was calling the handler() function multiple times when restarted so what I did was that I added the event listeners to the entire document and just removed the function handler() so that I wouldn't have to call that function just to get the event listeners and it worked: Here is the code that I made that worked:

let buttonColors = ["red", "blue", "green", "yellow"]
let gamePattern = []
let level = 1;
let userClickedPattern = []
let currentLevel = gamePattern.length
let started = false

document.addEventListener("keydown",function(event){
    if (!started){
        if (event.key=="a"){
            nextSequence();
            started = true
        }
    }
});

function checker(){
    if(userClickedPattern[currentLevel]===gamePattern[currentLevel]){
        if(userClickedPattern.length===gamePattern.length){
            level++;
            userClickedPattern =[];
            setTimeout(nextSequence, 1000)
        }
    }
    else if(userClickedPattern[currentLevel]!=gamePattern[currentLevel]){
        document.querySelector("h1").innerText = "Game Over. Press 'B' key to start again";
        let error1 = new Audio("sounds/wrong.mp3");
        error1.play()
        document.querySelector("body").classList.add("game-over")
        setTimeout(function(){
            document.querySelector("body").classList.remove("game-over")
        }, 200)
        startOver();
    }
}

    function nextSequence(){
        let randomChosenColor = buttonColors[Math.floor(Math.random()*4)];
        let sound1 = new Audio("sounds/"+randomChosenColor+".mp3");
        sound1.play()
        gamePattern.push(randomChosenColor)
        document.querySelector("h1").innerText = "Level "+(level);
        //STYLE
        document.querySelector("#"+randomChosenColor).classList.add("pressed");
        setTimeout(function (     {document.querySelector("#"+randomChosenColor).classList.remove("pressed")
        }, 200)
    
    }


for(let i = 0; i<document.querySelectorAll(".btn").length; i++){
    document.querySelectorAll(".btn")[i].addEventListener("click", function(){
        let userChosenColor = this.getAttribute("id");
        userClickedPattern.push(userChosenColor)
        //SOUND
        let sound1 = new Audio("sounds/"+userChosenColor+".mp3");
        sound1.play()
        //STYLE
        document.querySelector("#"+userChosenColor).classList.add("pressed");
        setTimeout(function ()    {document.querySelector("#"+userChosenColor).classList.remove("pressed")
        }, 200)
        checker();      
        console.log(gamePattern);
        console.log(userClickedPattern);     
    })      
}
    
function startOver(){
    gamePattern = [];
    userClickedPattern = [];
    level = 1;    
    started = false;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM