javascript - 测验应用程序上的 HTML/Javascript 速度慢/崩溃
问题描述
我在其他功能齐全的问答游戏中遇到了问题。在大约 5 或 6 个(共 10 个)问题之后,似乎出现了崩溃/加载缓慢的问题。这很奇怪,因为如果我在一秒钟内回答所有问题(不切实际,但要进行错误检查),就没有问题。但是,一旦我花“正常”时间回答问题,事情就会变慢,最终崩溃/卡住。
我曾尝试删除动画并调整我的 JS 的流程,但无济于事。如果有人有任何想法,他们将不胜感激!
这是网站本身的链接: https ://louparker.github.io/random-music-quiz/index.html
这是回购的链接: 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>
提前致谢!
解决方案
Oookey 原来你有一个非常大的递归问题。在倒计时间隔中,您将事件侦听器提供给选择,并像优秀的程序员一样清除间隔,但随后您忘记了自己处于forEach
. 所以当你稍后调用你的restartInterval
函数时,你实际上做了四次。我想你可以想象在 24 个区间同时运行的情况下,第六题会发生什么。
PS 当您使用间隔时,请始终检查是否只有您打算运行的那些正在运行。simple console.log()
正如您在下面的代码段中看到的那样,一种很好的检查方式。
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>