I have a problem with my otherwise fully functioning quiz game. There seems to be crashing/slow loading issues after around 5 or 6 (out of 10) questions. Its very strange because if I answer everything within a second (unrealistic but to error check), there is no problem. But as soon as I take a “normal” amount of time to answer questions, things slow down and it eventually crashes/gets stuck.
I have tried removing animations & adjusting the flow of my JS but to no avail. If anyone has any thoughts, they would be highly appreciated!
Here is a link to the site itself: https://louparker.github.io/random-music-quiz/index.html
Here is a link to the repo: https://github.com/louparker/random-music-quiz
const question = document.getElementById("question"); const choices = Array.from(document.getElementsByClassName("choice__text")); const scoreText = document.getElementById('score'); const timer = document.getElementById("timer"); const game = document.getElementById("game"); const loader = document.getElementById("loader"); const gameDifficulty = window.location.search.replace("?mode=", ""); /* STARTING GAME */ //game mechanics let currentQuestion = {}; let takingAnswers = true; let score = 0; let questionCounter = 0; let availableQuestions = {}; let fetchingData = true; let acceptingAnswers = true; //taking data from API fetch(`https://opentdb.com/api.php?amount=10&category=12&difficulty=${gameDifficulty}&type=multiple`) .then(res => { return res.json(); }) //taking question data from API and formatting it to be used .then((loadedQuestions) => { questions = loadedQuestions.results.map((loadedQuestion) => { const formattedQuestion = { question: loadedQuestion.question, }; //taking answer data and choosing random place for corrent and incorrent answers const answerChoices = [...loadedQuestion.incorrect_answers]; formattedQuestion.answer = Math.floor(Math.random() * 4) + 1; answerChoices.splice( formattedQuestion.answer - 1, 0, loadedQuestion.correct_answer ); answerChoices.forEach((choice, index) => { formattedQuestion['choice' + (index + 1)] = choice; }); return formattedQuestion; }); // timer //function to start the timer on end of current time or start of new question function restartInterval(){ let seconds = document.getElementById("timer").textContent; let countdown = setInterval(function() { seconds--; //new question timer restart function choices.forEach((choice) => { choice.addEventListener('click', (e) => { clearInterval(countdown); timer.innerText = "30"; restartInterval(); }); }); //timer reaches zero restart function document.getElementById("timer").textContent = seconds; if (seconds <= 0) { clearInterval(countdown); getNewQuestion(); timer.innerText = "30"; restartInterval(); } }, 1000); } //confirming game data is all loaded, showing the game page and removing the loading screen fetchingData = false; setTimeout( () => { game.classList.remove("hidden"); loader.classList.add("hidden"); startGame(); restartInterval(); }, 1000); }) .catch((err) => { console.error(err); }); //base set up for loading the game page const startGame = () => { questionCounter = 0; score = 0; availableQuestions = [...questions]; getNewQuestion(); }; //giving specific scores based on gae difficulty const levelScore = gameDifficulty === "easy" ? 10 : gameDifficulty === "medium" ? 20 : 30; const maxQuestions = 10; let baseUrl ="https://louparker.github.io/random-music-quiz"; //checking if answers are correct or not choices.forEach((choice) => { choice.addEventListener('click', (e) => { if (!takingAnswers) return; acceptingAnswers = false; const selectedChoice = e.target; const selectedAnswer = selectedChoice.dataset.number; const classToApply = selectedAnswer == currentQuestion.answer ? "correct" : "incorrect"; if (classToApply === "correct") { incrementScore(levelScore); } selectedChoice.parentElement.classList.add(classToApply); setTimeout(() => { selectedChoice.parentElement.classList.remove(classToApply); getNewQuestion(); }, 1000); }); }); //adds specified score to score element const incrementScore = (num) => { score += num; scoreText.innerHTML = score; }; //grabbing new question data and assigning score for gameover page const getNewQuestion = () => { if (availableQuestions.length === 0 || questionCounter >= maxQuestions) { localStorage.setItem("mostRecentScore", score); return window.location.replace(`${baseUrl}/gameover.html?mode=${gameDifficulty}`); } questionCounter ++; const questionIndex = Math.floor(Math.random() * availableQuestions.length); currentQuestion = availableQuestions[questionIndex]; question.innerHTML = currentQuestion.question; choices.forEach((choice) => { const number = choice.dataset.number; choice.innerHTML = currentQuestion['choice' + number]; }); availableQuestions.splice(questionIndex, 1); takingAnswers = true; };
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="The game page of quiz game about music"> <title>Game</title> <!-- styles --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous"> <link rel="stylesheet" href="https://louparker.github.io/random-music-quiz/assets/css/app.css"> <link rel="stylesheet" href="https://louparker.github.io/random-music-quiz/assets/css/game.css"> </head> <body> <main> <div class="container-fluid text-center d-flex"> <div id="loader" class="spinner"> <div class="dot1"></div> <div class="dot2"></div> </div> <div id="game" class="hidden"> <!-- heads up display (HUD) --> <div class="row"> <div class="col-12"> <div class="hud__container d-flex"> <div class="hud__btn d-flex hvr-pulse-shrink"> <a href="index.html" class="exit__btn">X</a> </div> <div class="hud__btn d-flex"> <p class="timer" id="timer">30</p> <span class="timer__label">TIME</span> </div> <div class="hud__btn d-flex"> <p class="scoreboard" id="score">0</p> <span class="score__label">SCORE</span> </div> </div> </div> </div> <!-- game question --> <div class="row"> <div class="col-12"> <div class="game__question d-flex"> <p class="question-text" id="question"></p> </div> </div> </div> <!-- answer choices --> <div class="row"> <div class="col-12"> <div class="answer-choices__container d-flex"> <div class="choice__container d-flex"> <p class="choice__text" data-number="1"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="2"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="3"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="4"> </p> </div> </div> </div> </div> </div> </div> </main> <!-- scripts --> <!--<script src="js/game.js"></script>--> </body> </html>
Thanks in advance!
Advertisement
Answer
Oookey it turns out you have a really big recursion problem.
In your countdown interval you give event listeners to the choices and you clear the interval like a good programmer, but then you have forgot that you are in a forEach
. So when later you call your restartInterval
function you actually do it four times.
I think that you can imagine what happens at the sixth question with the 24 intervals running at the same time.
P.S. when you work with intervals always check that only those are running that you intended to run. A god way of checking is a simple console.log()
as you see in the snippet down below.
const question = document.getElementById("question"); const choices = Array.from(document.getElementsByClassName("choice__text")); const scoreText = document.getElementById('score'); const timer = document.getElementById("timer"); const game = document.getElementById("game"); const loader = document.getElementById("loader"); const gameDifficulty = window.location.search.replace("?mode=", ""); /* STARTING GAME */ //game mechanics let currentQuestion = {}; let takingAnswers = true; let score = 0; let questionCounter = 0; let availableQuestions = {}; let fetchingData = true; let acceptingAnswers = true; //taking data from API fetch(`https://opentdb.com/api.php?amount=10&category=12&difficulty=${gameDifficulty}&type=multiple`) .then(res => { return res.json(); }) //taking question data from API and formatting it to be used .then((loadedQuestions) => { questions = loadedQuestions.results.map((loadedQuestion) => { const formattedQuestion = { question: loadedQuestion.question, }; //taking answer data and choosing random place for corrent and incorrent answers const answerChoices = [...loadedQuestion.incorrect_answers]; formattedQuestion.answer = Math.floor(Math.random() * 4) + 1; answerChoices.splice( formattedQuestion.answer - 1, 0, loadedQuestion.correct_answer ); answerChoices.forEach((choice, index) => { formattedQuestion['choice' + (index + 1)] = choice; }); return formattedQuestion; }); // timer //function to start the timer on end of current time or start of new question function restartInterval(){ let seconds = document.getElementById("timer").textContent; let countdown = setInterval(function() { seconds--; console.log(seconds); //new question timer restart function choices.forEach((choice) => { choice.addEventListener('click', (e) => { clearInterval(countdown); timer.innerText = "30"; restartInterval(); }); }); //timer reaches zero restart function document.getElementById("timer").textContent = seconds; if (seconds <= 0) { clearInterval(countdown); getNewQuestion(); timer.innerText = "30"; restartInterval(); } }, 1000); } //confirming game data is all loaded, showing the game page and removing the loading screen fetchingData = false; setTimeout( () => { game.classList.remove("hidden"); loader.classList.add("hidden"); startGame(); restartInterval(); }, 1000); }) .catch((err) => { console.error(err); }); //base set up for loading the game page const startGame = () => { questionCounter = 0; score = 0; availableQuestions = [...questions]; getNewQuestion(); }; //giving specific scores based on gae difficulty const levelScore = gameDifficulty === "easy" ? 10 : gameDifficulty === "medium" ? 20 : 30; const maxQuestions = 10; let baseUrl ="https://louparker.github.io/random-music-quiz"; //checking if answers are correct or not choices.forEach((choice) => { choice.addEventListener('click', (e) => { if (!takingAnswers) return; acceptingAnswers = false; const selectedChoice = e.target; const selectedAnswer = selectedChoice.dataset.number; const classToApply = selectedAnswer == currentQuestion.answer ? "correct" : "incorrect"; if (classToApply === "correct") { incrementScore(levelScore); } selectedChoice.parentElement.classList.add(classToApply); setTimeout(() => { selectedChoice.parentElement.classList.remove(classToApply); getNewQuestion(); }, 1000); }); }); //adds specified score to score element const incrementScore = (num) => { score += num; scoreText.innerHTML = score; }; //grabbing new question data and assigning score for gameover page const getNewQuestion = () => { if (availableQuestions.length === 0 || questionCounter >= maxQuestions) { localStorage.setItem("mostRecentScore", score); return window.location.replace(`${baseUrl}/gameover.html?mode=${gameDifficulty}`); } questionCounter ++; const questionIndex = Math.floor(Math.random() * availableQuestions.length); currentQuestion = availableQuestions[questionIndex]; question.innerHTML = currentQuestion.question; choices.forEach((choice) => { const number = choice.dataset.number; choice.innerHTML = currentQuestion['choice' + number]; }); availableQuestions.splice(questionIndex, 1); takingAnswers = true; };
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="The game page of quiz game about music"> <title>Game</title> <!-- styles --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous"> <link rel="stylesheet" href="https://louparker.github.io/random-music-quiz/assets/css/app.css"> <link rel="stylesheet" href="https://louparker.github.io/random-music-quiz/assets/css/game.css"> </head> <body> <main> <div class="container-fluid text-center d-flex"> <div id="loader" class="spinner"> <div class="dot1"></div> <div class="dot2"></div> </div> <div id="game" class="hidden"> <!-- heads up display (HUD) --> <div class="row"> <div class="col-12"> <div class="hud__container d-flex"> <div class="hud__btn d-flex hvr-pulse-shrink"> <a href="index.html" class="exit__btn">X</a> </div> <div class="hud__btn d-flex"> <p class="timer" id="timer">30</p> <span class="timer__label">TIME</span> </div> <div class="hud__btn d-flex"> <p class="scoreboard" id="score">0</p> <span class="score__label">SCORE</span> </div> </div> </div> </div> <!-- game question --> <div class="row"> <div class="col-12"> <div class="game__question d-flex"> <p class="question-text" id="question"></p> </div> </div> </div> <!-- answer choices --> <div class="row"> <div class="col-12"> <div class="answer-choices__container d-flex"> <div class="choice__container d-flex"> <p class="choice__text" data-number="1"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="2"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="3"> </p> </div> <div class="choice__container d-flex"> <p class="choice__text" data-number="4"> </p> </div> </div> </div> </div> </div> </div> </main> <!-- scripts --> <!--<script src="js/game.js"></script>--> </body> </html>