Reputation: 737
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.
Upvotes: 2
Views: 186
Reputation: 737
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;
}
Upvotes: 1
Reputation: 2266
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:
click
handler function cannot be an anonymous function because we'll need to reference it again later.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
}
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
}
Upvotes: 3