How to stop an interval with clearInterval and continue it later?

Asked

Viewed 62 times

-1

I’m making a code to make a typing game: by typing the word correctly the user gains an extra time in the time being decreased and gains 1 point, and loses if he reaches 0s.

For example: if you get the word right when it’s in 7s, that time goes to 9 seconds.

But when the word hit event is triggered, I clear the interval and Seto the variable corresponding to the interval to call the function again, now with the seconds to add to the time (in case 2s)but the game starts to behave strangely and does not do what the function should continue to do, so I think this function is not being called.

The function to setInterval can only be called once by it? Or else when clearing the interval has no way to continue it?

var word = document.getElementById("word");
var wordTyped = document.getElementById("text");
var time = document.getElementById("time");
var score = document.getElementById("score");
var endGame = document.querySelector(".end-game-container");
const settingsBtn = document.getElementById("settings-btn");
const difficultySettings = document.getElementById("settings");
var secondsInterval = setInterval(updateTime, 1000, 0);
var currentTime = Number(
  time.textContent.substring(0, time.textContent.length - 1)
);
const words = [
  "bad",
  "view",
  "world",
  "release",
  "caracteristic",
  "grass",
  "homeland",
  "building",
  "juice",
  "illness",
];

//FUNÇÃO QUE GERA UMA PALAVRA NOVA NA TELA AO ACERTAR A ANTERIOR
function randomWord(secondsToAdd) {
  if (secondsToAdd > 0) {
    clearInterval(secondsInterval);
    secondsInterval = setInterval(updateTime, 1000, secondsToAdd);//PARTE QUE O CÓDIGO PARA DE FUNCIONAR DE FORMA NORMAL JÁ QUE OS SEGUNDOS A ADICIONAR É MAIOR QUE 0 POIS É CHAMADA QUANDO ACERTOU A PALAVRA
  }
  let selectedWord = words[Math.floor(Math.random() * words.length)];
  word.textContent = selectedWord;
}

function lostTheGame() {
  endGame.style.display = "flex";
  endGame.innerHTML = `<h1>Time ran out</h1> <p> Your final score is ${score.innerHTML} <button onclick=reload() > Reload </button>`;
}

function reload() {
  wordTyped.value = "";
  time.textContent = "10s";
  score.textContent = "0";
  randomWord(0);
  updateTime(0);
  endGame.style.display = "none";
}
//FUNÇÃO CORRESPONDENTE AO INTERVALO
function updateTime(secondsToAdd) {
  time.textContent = `${currentTime}s`;
  if (currentTime == 0) {
    clearInterval(secondsInterval);
    lostTheGame();
  }
  currentTime += secondsToAdd;
  currentTime--;
}

wordTyped.addEventListener("keyup", () => {
  if (wordTyped.value == word.textContent) {
    randomWord(2);
    wordTyped.value = "";
    score.innerHTML = +score.innerHTML + 1;
  }
});
randomWord(0);

settingsBtn.addEventListener("click", () => {
  difficultySettings.classList.toggle("hide");
});
* {
  box-sizing: border-box;
}

body {
  background-color: #2c3e50;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  margin: 0;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
}

button {
  cursor: pointer;
  font-size: 14px;
  border-radius: 4px;
  padding: 5px 15px;
}

select {
  width: 200px;
  padding: 5px;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  border-radius: 0;
  background-color: #a7c5e3;
}

select:focus,
button:focus {
  outline: 0;
}

.settings-btn {
  position: absolute;
  bottom: 30px;
  left: 30px;
}

.settings {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.3);
  height: 70px;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translateY(0);
  transition: transform 0.3s ease-in-out;
}

.settings.hide {
  transform: translateY(-100%);
}

.container {
  background-color: #34495e;
  padding: 20px;
  border-radius: 4px;
  box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3);
  color: #fff;
  position: relative;
  text-align: center;
  width: 500px;
}

h2 {
  background-color: rgba(0, 0, 0, 0.3);
  padding: 8px;
  border-radius: 4px;
  margin: 0 0 40px;
}

h1 {
  margin: 0;
}

input {
  border: 0;
  border-radius: 4px;
  font-size: 14px;
  width: 300px;
  padding: 12px 20px;
  margin-top: 10px;
}

.score-container {
  position: absolute;
  top: 60px;
  right: 20px;
}

.time-container {
  position: absolute;
  top: 60px;
  left: 20px;
}

.end-game-container {
  background-color: inherit;
  display: none;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css"
      integrity="sha256-+N4/V/SbAFiW1MPBCXnfnP9QSN3+Keu+NlB+0ev/YKQ="
      crossorigin="anonymous"
    />
    <link rel="stylesheet" href="style.css" />
    <title>Speed Typer</title>
  </head>
  <body>
    <button id="settings-btn" class="settings-btn">
      <i class="fas fa-cog"></i>
    </button>

    <div id="settings" class="settings">
      <form id="settings-form">
        <div>
          <label for="difficulty">Difficulty</label>
          <select id="difficulty">
            <option value="easy">Easy</option>
            <option value="medium">Medium</option>
            <option value="hard">Hard</option>
          </select>
        </div>
      </form>
    </div>

    <div class="container">
      <h2>‍ Speed Typer ‍</h2>
      <small>Type the following:</small>

      <h1 id="word"></h1>

      <input
        type="text"
        id="text"
        autocomplete="off"
        placeholder="Type the word here..."
      />

      <p class="time-container">Time left: <span id="time">10s</span></p>

      <p class="score-container">Score: <span id="score">0</span></p>

      <div id="end-game-container" class="end-game-container"></div>
    </div>

    <script src="script.js"></script>
  </body>
</html>

1 answer

1


The problem, in my view, is that you are mixing different responsibilities in the same roles. For example, the function that chooses the random word is also responsible for updating the count, has more than one place that sums the seconds to the current time, etc.

Instead of trying to fix your code, I decide to rewrite a part of it, to better separate the responsibilities of each function. In the example below I removed the CSS and part of the HTML to focus only on the algorithm itself (but then you add what you need):

var word = document.getElementById("word");
var wordTyped = document.getElementById("text");
var timeLeft = document.getElementById("time-left");
var score = document.getElementById("score");
var endGame = document.querySelector(".end-game-container");
const words = [ "bad", "view", "world", "release", "caracteristic", "grass", "homeland", "building", "juice", "illness" ];
var currentWordIndex = -1;
var secondsInterval = null;

// número aleatório entre min e max
function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// escolhe a palavra aleatória e atualiza o input - e só
function chooseWord() {
  // soma um valor entre 1 e (tamanho do array - 1), assim a próxima palavra não será repetida
  currentWordIndex = (currentWordIndex + randomInteger(1, words.length - 1)) % words.length;
  word.textContent = words[currentWordIndex];
  wordTyped.value = "";
}

// atualiza a contagem regressiva (diminui 1 do tempo e vê se chegou a zero)
function countdown() {
  var currentTime = parseInt(timeLeft.textContent) - 1;
  timeLeft.textContent = currentTime;
  if (currentTime == 0) {
    clearInterval(secondsInterval); // se terminou o jogo, para a contagem regressiva
    lostTheGame();
  }
}

function lostTheGame() {
  endGame.style.display = "flex";
  endGame.innerHTML = `<h1>Time ran out</h1> <p> Your final score is ${score.innerHTML} <button onclick=reload() > Reload </button>`;
}

function reload() { // reseta as configurações do jogo
  wordTyped.value = "";
  timeLeft.textContent = "10";
  score.textContent = "0";
  chooseWord();
  endGame.style.display = "none";
  // crio o interval aqui - e somente aqui
  secondsInterval = setInterval(countdown, 1000);
}

function addTime(seconds) { // adiciona segundos ao tempo atual
  timeLeft.textContent = parseInt(timeLeft.textContent) + seconds;
}

wordTyped.addEventListener("keyup", function() {
  // se acertou, adiciona 2 segundos e escolhe nova palavra
  if (wordTyped.value == word.textContent) {
    addTime(2);
    chooseWord();
    score.innerHTML = parseInt(score.innerHTML) + 1;
  }
});

// o jogo começa com as configurações iniciais (que no fundo é o mesmo que um reload)
reload();
<div class="container">
  <small>Type the following:</small>
  <h1 id="word"></h1>
  <input type="text" id="text" autocomplete="off" placeholder="Type the word here...">
  <p class="time-container">Time left: <span id="time-left">10</span>s</p>
  <p class="score-container">Score: <span id="score">0</span></p>
  <div id="end-game-container" class="end-game-container"></div>
</div>

I also changed the way of choosing the random word, to avoid that there is repetition (the way you did, there was a chance to be the same).

And in the span which contains the remaining time, I left only the numeric value (the "s" left out), so it is easier to take only the number in the code.


Other option

Instead of setInterval, you could use setTimeout. So you don’t have to worry about cleaning the timer, and you only call him again if it’s not zeroed:

var word = document.getElementById("word");
var wordTyped = document.getElementById("text");
var timeLeft = document.getElementById("time-left");
var score = document.getElementById("score");
var endGame = document.querySelector(".end-game-container");
const words = [ "bad", "view", "world", "release", "caracteristic", "grass", "homeland", "building", "juice", "illness" ];
var currentWordIndex = -1;

function randomInteger(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function chooseWord() {
  currentWordIndex = (currentWordIndex + randomInteger(1, words.length - 1)) % words.length;
  word.textContent = words[currentWordIndex];
  wordTyped.value = "";
}

function countdown() {
  var currentTime = parseInt(timeLeft.textContent) - 1;
  timeLeft.textContent = currentTime;
  // se zerou, acaba o jogo; senão, continua a contagem regressiva
  if (currentTime == 0) {
    lostTheGame();
  } else setTimeout(countdown, 1000);
}

function lostTheGame() {
  endGame.style.display = "flex";
  endGame.innerHTML = `<h1>Time ran out</h1> <p> Your final score is ${score.innerHTML} <button onclick=reload() > Reload </button>`;
}

function reload() {
  wordTyped.value = "";
  timeLeft.textContent = "10";
  score.textContent = "0";
  chooseWord();
  endGame.style.display = "none";
  setTimeout(countdown, 1000);
}

function addTime(seconds) {
  timeLeft.textContent = parseInt(timeLeft.textContent) + seconds;
}

wordTyped.addEventListener("keyup", function() {
  if (wordTyped.value == word.textContent) {
    addTime(2);
    chooseWord();
    score.innerHTML = parseInt(score.innerHTML) + 1;
  }
});

reload();
<div class="container">
  <small>Type the following:</small>

  <h1 id="word"></h1>

  <input type="text" id="text" autocomplete="off" placeholder="Type the word here...">

  <p class="time-container">Time left: <span id="time-left">10</span>s</p>

  <p class="score-container">Score: <span id="score">0</span></p>
  <div id="end-game-container" class="end-game-container"></div>
</div>

To learn more about the difference between setTimeout and setInterval, read here.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.