Skip to content

HTML/Javascript slow/crashing on quiz app

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/[email protected]/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!

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/[email protected]/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>