0

I want this reset button to clear/reset the timer. I need it to reset because when the user presses the start button more then once it set´s two timers one that was started previously and the one that was just started.

I´m going to use clearInterval to fix this.

Here is my timer:

function startTimer(duration, display) {
  var timer = duration, minutes, seconds;
  setInterval(function () {
      minutes = parseInt(timer / 60, 10);
      seconds = parseInt(timer % 60, 10);

      minutes = minutes < 10 ? "0" + minutes : minutes;
      seconds = seconds < 10 ? "0" + seconds : seconds;

      display.textContent = minutes + ":" + seconds;

      if (--timer < 0) {
          timer = duration;
      }
      function resetTimer(){
          clearInterval(timer);
      }
  }, 1000);
}

function start5Timer() {
  var fiveMinutes = 60 * 5,
      display = document.querySelector('#time');
  startTimer(fiveMinutes, display);
};
<body>
    <div><span id="time">05:00</span> minutes!
    <button onclick="start5Timer()">start</button>
    <button onclick="resetTimer()">reset</button>
    </div>
</body>
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • It isn´t a JS fiddle but Here is a link to my code with the w3school v3.6 tryit editor: https://www.w3schools.com/code/tryit.asp?filename=GJQGNII3DAM6 –  Oct 15 '20 at 16:00
  • 2
    And yes, don't use w3schools as a learning resource. – Roko C. Buljan Oct 15 '20 at 16:02
  • 1
    You need to set a variable to the result of calling `setInterval` and pass that variable to `clearInterval` – Heretic Monkey Oct 15 '20 at 16:03
  • Does this answer your question? [Stop setInterval call in JavaScript](https://stackoverflow.com/questions/109086/stop-setinterval-call-in-javascript) – Heretic Monkey Oct 15 '20 at 16:03
  • @HereticMonkey When I press start more then one time the timer shows the previous and current timer set. I now realize that this might not help solve the problem should I use a display property, like after they press reset the timer won't show the previous timer set and only the current? But then I might have to use the display property multiple times, because the user presses start for the same timer multiple times. Is there an easier way to do this? –  Oct 15 '20 at 16:57
  • If you don't want multiple concurrent timers running, disable the start button while a timer is running. Function `resetTimer` must be pulled into a scope where it is visible by the reset button click handler. When starting a timer the timer ID must be stored in a variable, to be used by the `resetTimer` function. – Ben Aston Oct 15 '20 at 17:17
  • There are a cornucopia of questions and answers on this site about stopping/resetting timers. [How do I reset the setInterval timer?](https://stackoverflow.com/q/19874555/215552), for instance, must have come up in a search. The question is, why have two buttons? Have your start button become a reset button. Have the click handler check to see if it's running and if it is, reset it, otherwise, start one. – Heretic Monkey Oct 16 '20 at 15:29

2 Answers2

0

You can use timerId in the global scope. Set it's value when the interval starts and use the timerId to clear the interval and start a new timer starting from 05:00

<html>

  <body>
    <div><span id="time">05:00</span> minutes!
      <button onclick="start5Timer()">start</button>
      <button onclick="resetTimer()">reset</button>
    </div>
  </body>

  <script>
    let timerId;
    function startTimer(duration, display) {
      var timer = duration,
        minutes, seconds;
      timerId = setInterval(function() {
        minutes = parseInt(timer / 60, 10);
        seconds = parseInt(timer % 60, 10);

        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;

        display.textContent = minutes + ":" + seconds;

        if (--timer < 0) {
          timer = duration;
        }
      }, 1000);
    }
    
     function resetTimer() {
          clearInterval(timerId);
          start5Timer();
        }

    function start5Timer() {
      var fiveMinutes = 60 * 5,
      display = document.querySelector('#time');
       startTimer(fiveMinutes, display);
    };

  </script>

</html>
nain12
  • 44
  • 4
  • It looks like the timer loops, do you know how I can fix this? –  Oct 16 '20 at 00:34
  • If you want to just clear the interval and not reset it back to its original value, then you can avoid it by not calling start5timer inside the resetTimer function. – nain12 Oct 16 '20 at 03:15
0

Here's a timer implementation using unidirectional data-flow and immutable state.

There are three actions: start, reset and tick. When dispatched to the store, the state is updated.

The timer controls its own rendering using requestAnimationFrame.

function createStore(initialState, reducers) {
  let state = Object.freeze(initialState)
  const reducerMap = reducers.reduce(
    (p, { type, reducer }) => (p[type] ? p[type].push(reducer) : (p[type] = [reducer]), p), {})

  function dispatch(action, ...args) {
    state = reducerMap[action].reduce((p, c) => c(p, ...args), state)
  }

  return {
    dispatch,
    getState() {
      return state
    }
  }
}

const toTwoDigitString = (n) => (n + "").padStart(2, "0")

const formatForDisplay = (seconds) =>
  `${toTwoDigitString(~~(seconds / 60))}:${toTwoDigitString(
    ~~(seconds % 60)
  )} minutes`

const tick = (store) =>
  store.getState().elapsedSeconds >= store.getState().durationSeconds
    ? clearInterval(store.getState().timerId)
    : store.dispatch(ACTION_TICK)

const startTimer = (store) => {
  if (store.getState().timerId !== null) return
  const timerId = setInterval(() => tick(store), 1000)
  store.dispatch(ACTION_START, timerId)
}

const resetTimer = (store) => {
  clearInterval(store.getState().timerId)
  store.dispatch(ACTION_RESET)
}

const render = ({ displayEl, durationSeconds, elapsedSeconds }) =>
  (displayEl.innerHTML = formatForDisplay(durationSeconds - elapsedSeconds))

const ACTION_START = "ACTION_START"
const ACTION_RESET = "ACTION_RESET"
const ACTION_TICK = "ACTION_TICK"

const reducerStart = {
  type: ACTION_START,
  reducer(state, timerId) {
    return {
      ...state,
      timerId
    }
  }
}

const reducerReset = {
  type: ACTION_RESET,
  reducer(state) {
    return {
      ...state,
      timerId: null,
      elapsedSeconds: 0
    }
  }
}

const reducerTick = {
  type: ACTION_TICK,
  reducer: (state) => {
    return {
      ...state,
      elapsedSeconds: ++state.elapsedSeconds
    }
  }
}

function createTimer({ selector, durationSeconds }) {
  const timerEl = document.querySelector(selector)

  const initialState = {
    timerId: null,
    displayEl: timerEl.querySelector("#time"),
    durationSeconds,
    elapsedSeconds: 0
  }

  const store = createStore(initialState, [
    reducerStart,
    reducerReset,
    reducerTick
  ])

  const start = () => startTimer(store)
  const reset = () => resetTimer(store)

  timerEl.querySelector("button#start").addEventListener("click", start)
  timerEl.querySelector("button#reset").addEventListener("click", reset)

  const previousState = null
  ;(function renderLoop() {
    if (previousState !== store.getState()) render(store.getState())
    requestAnimationFrame(renderLoop)
  })()
}

createTimer({ selector: "#timer", durationSeconds: 2 * 5 })
* {
  font-family: sans-serif;
  color: #dc1a1a !important;
  font-size: 1.1rem;
  padding: 10px;
  margin: 10px;
}
<div id="timer">
  <span id="time">The time is:</span>
  <button id="start">start</button>
  <button id="reset">reset</button>
</div>
Ben Aston
  • 53,718
  • 65
  • 205
  • 331