Timer pause functionality not working as expected due to recursion

When I click the pause button on my Timer, it correctly pauses the countdown. However, when I click the button again, the timer starts from the beginning instead of where it was paused. How can I prevent this issue and ensure that the pause function works correctly?

    var label = document.getElementById("exerciseLabel");
    var button = document.getElementById("pauseButton");
    var counter = document.getElementById("exerciseCounter");
    var routine = document.getElementById("info");
    var current = 0;
    var playing = false;
    var countdownTimer = null;
    var workout =
    {
      "title": "Full Body",
      "exercises":
      [
        {
          "name": "Push Ups",
          "duration": 3,
          "break": 3
        },
        {
          "name": "Squats",
          "duration": 3,
          "break": 3
        },
        {
          "name": "Running in place",
          "duration": 3,
          "break": 3
        }
      ]
    };
    
    // LOOPING TIMER FUNCTION
    // Initialise virtual trainer.
    init();
    
    /**
    * Bind loop start to click on button.
    *
    * @return {void}
    */
    function init()
    {
      loop();
      button.addEventListener("click", toggle);
    }
    
    /**
    * Play / Stop exercising.
    *
    * @return {void}
    */
    function toggle()
    {
      if (playing)
      {
        pause();
      }
      else
      {
        loop();
      }
    }
    
    /**
    * Reset timer. <--SHOULD BE PAUSE
    *
    * @return {void}
    */
    function pause()
    {
      playing = false;
      setLabel("Paused");
      //setCounter('--')
      if (countdownTimer)
      {
        clearTimeout(countdownTimer); // FIGURE OUT HOW NOT TO CLEAR
      }
    }
    
    // TIMER FUNCTION
    /**
    * Timer loop function
    *
    * @return {void}
    */
    function loop()
    {
      playing = true;
    
      // Change button label
      setButtonText("Pause");
    
      // Get current exercise
      var exercise = workout.exercises[current];
    
      // If out of the exercises Array's bounds, call 'done'
      if (!exercise)
      {
        return done();
      }
      // Otherwise run countdown and update UI.
      countdown(exercise.duration, exercise.name, function ()
      {
        countdown(exercise.break, "Break", function ()
        {
          // Next exercise.
          current++;
          // Re-iterate until finished.
          loop();
        });
      });
    }
    
    /**
    * Exercise session is now over.
    *
    * @return {void}
    */
    function done()
    {
      pause();
      document.getElementById("feedbackScreen").style.display = "block";
    }
    
    /**
    * Recursive timer function.
    *
    * @param  {Number} seconds
    * @param  {String} label
    * @param  {Function} callback
    * @return {void}
    */
    function countdown(seconds, label, callback)
    {
      setLabel(label);
      setCounter(seconds);
    
      // Set timeout to next second
      countdownTimer = setTimeout(function ()
      {
        // Decrease seconds.
        seconds--;
    
        // Check whether the countdown is over - execute callback if so.
        if (seconds <= 0)
        {
          return callback();
        }
    
        // Call itself with a lower number otherwise.
        countdown(seconds, label, callback);
      }, 1000); // (1 sec).
    }
    
    /**
    * Set counter text.
    *
    * @param  {Number} val
    * @return {void}
    */
    function setCounter(val)
    {
      counter.innerHTML = val;
    }
    
    /**
    * Set label text.
    *
    * @param  {String} text
    * @return {void}
    */
    function setLabel(text)
    {
      if (label.textContent === text)
      {
        return;
      }
      label.innerHTML = text;
    }
    
    /**
    * Set button text.
    *
    * @param  {String} text
    * @return {void}
    */
    function setButtonText(label)
    {
      button.innerHTML = label;
    }
    <div id="exerciseLabel"></div>
    <button id="pauseButton"></button>
    <div id="exerciseCounter"></div>
    <div id="info"></div>

Answer №1

var isPaused = false;
var breakDisplay = false;

 window.onload = function() {
    initializeCounter();
 };

 var counting;
 var counter;
 var currentExercise = 0
var exerciseElement = document.getElementById("exerciseLabel");
  var workoutRoutine =
{
  "title": "Full Body",
  "exercises":
  [
    {
      "name": "Push Ups",
      "duration": 1,
      "break": 2
    },
    {
      "name": "Squats",
      "duration": 2,
      "break": 0.5
    },
    {
      "name": "Running in place",
      "duration": 0.25,
      "break": 1
    }
  ]
};

 function initializeCounter() {
 
    counting = workoutRoutine.exercises[0].duration*60; 
    breakCounting = workoutRoutine.exercises[0].break*60;
    initiateTimer();
  }

 function timerUpdate() {
    
    if(breakDisplay){
      breakCounting = breakCounting - 1;
    }
    else{
    counting = counting -1;
    }
    
    var secondsPassed = (breakDisplay ? breakCounting : counting ) % 60;
    
    var minutesPassed = Math.floor((breakDisplay ? breakCounting : counting ) / 60);
    var hoursPassed = Math.floor(minutesPassed / 60);
    minutesPassed %= 60;
    
    document.getElementById("timer").innerHTML = ""+ hoursPassed + ":" + minutesPassed + ":" + secondsPassed
    displayFunction();

   if(secondsPassed === 0 && minutesPassed === 0 && hoursPassed === 0)
   {
    //After duration of exercise or break
      if(!(currentExercise === workoutRoutine.exercises.length - 1 && breakDisplay === true)){
      // Display the next exercise or break
      breakDisplay = !breakDisplay;
      displayFunction();
        if(!breakDisplay){
        currentExercise = currentExercise + 1 ;
        //set break duration
        breakCounting = (workoutRoutine.exercises[currentExercise].break)*60;
        }
        else{
        //set next exercise duration
        if(currentExercise < workoutRoutine.exercises.length - 1){
        counting = (workoutRoutine.exercises[currentExercise + 1].duration)*60;
        }
        }
       }
      else
       {
        // All the exercises have already been displayed
        clearInterval(counter);
        pauseButton.innerHTML = "end";
       }   
     
      }
      
      }
      
function initiateTimer(){
counter = setInterval(timerUpdate, 100); //(I put 100 to make it quick) 1000 will  run it every 1 second
}
  
function controlPauseOrResume(){
  isPaused = !isPaused
  if(pauseButton.innerHTML !== "end"){
    if(isPaused){
    clearInterval(counter);
    pauseButton.innerHTML = "resume"
  }
  else {
        launchTimer();
        pauseButton.innerHTML = "pause"
    }

  }
  }
  
  function displayFunction(){
    if(!breakDisplay){
    exerciseElement.innerHTML = workoutRoutine.exercises[currentExercise].name
    }
    else{
    exerciseElement.innerHTML = "break"
    }
  }
<script type="text/javascript">
 
 </script>


<div id="timer"></div>

<div id="exerciseLabel"></div>
<button id="pauseButton" onclick="controlPauseOrResume()">pause</button>
<div id="exerciseCounter"></div>
<div id="info"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Changing the React state within an event listener connected to a hook can disrupt the default behavior

I've implemented a React hook to handle clicks outside of a specified div. Inside the hook, an event listener is attached and the component's state is updated in the event listener callback. However, this setup interferes with the default behavio ...

Received a JSON value that cannot be converted into a tuple of two numbers in Typescript

I'm a beginner when it comes to TypeScript and I am struggling to find the solution to my question. In my project, there is a config.json file structured like this: { "api": { "baseUrl": "http://localhost:9000/api", "list": { "itemsP ...

The Mongoose findOneAndUpdate method will only return the newly added document when using the $push

Provided here is a structured representation of my "profile" collection: { _id : ObjectId("2bb0fad110"), name : "Tommy", defaultrates : [ {_id : ObjectId("444cbcfd52"), rate : 35.0, raisedOn : "5/2/2009"}, {_ ...

How to launch a document in a new tab using AngularJS?

LIVE DEMO When given the URI of a file, my goal is to open it in a new tab instead of a new window. It appears that using $window.open(uri, '_blank') is not an option. I attempted this workaround: var link = angular.element('<a ...

Encountering an issue when attempting to create a status command using discord.js

I encountered an issue while developing a status command for my discord bot, which is intended to display title, description, and some fields. The error occurred when I was working with discord.js v13 and Node.js v16.14.2. Below is the snippet of my code ...

Having trouble transmitting information to a php script using $.post()?

I have set up a shopping cart functionality on my website. Here's how it works: On the catalog page, there are four products, each with its own "Add to Cart" button. When a user clicks on one of these buttons, an onClick attribute calls the addToCart( ...

Unlimited scrolling feature in Ionic using JSON endpoint

Trying to create an Ionic list using data from a JSON URL. JSON Code: [{"id":"1","firstName":"John", "lastName":"Doe"}, {"id":"2","firstName":"Anna", "lastName":"Smith"}, {"id":"3","firstName":"Peter", "lastName":"Jones"},{......................}] app.j ...

Converting an array object into a specific format: step-by-step guide

The data I am receiving from the backend is in an array object format. How can I transform it into a particular data format? For example: data = [ { name: value, age: value, phoneNumber: value, email: value }, { name: value, age: value, phoneNumber: va ...

Encountered a null error while utilizing ES6 computed state in React components

In my .jsx file, I am encountering an issue with the following code block: <tr> {!isEmpty(content) && content.map(o => { if(o.sortKey){ console.log(this.state[`order${o.sortKey}`]) } })} </tr> After making chan ...

Issue with OpenWeathermap country codes: "city was not located"

I've been working on coding a weather journal app and I'm encountering an issue. When using the fetch method with the URL of openweathermap.com, passing the zip code and country code, it seems to only accept the US as the country code. Despite tr ...

Do JavaScript functions in separate <script> tags exist in the global scope?

Here's a scenario I'm dealing with in an HTML file: <script> var my_function = function(param) { alert(param); } </script> <div> <!-- snip --> <script> $(function() { my_function("Hello wor ...

How can I dynamically display a component in Angular using programming?

Is there a method in Angular to dynamically render a component into a DOM element using code? For instance, I know that in React you can utilize ReactDOM.render to transform a component into a DOM element. Is there an equivalent functionality available in ...

Different ways to pass an input file type through JavaScript within Joomla modules

I need to transfer a file from JavaScript to a PHP file. In my PHP file, I have the following form: <form action="" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file ...

How can the actual values from the Repeater be retrieved using Protractor, instead of the Elements?

As I develop a Protractor script to test my quiz game, which involves displaying random questions and answers, I am faced with the challenge of identifying the correct answer. This information is not directly available as an element on the page, so I need ...

What is the best way to configure shared functions that can be used across all of my test suites when working with Protractor and Selenium?

Currently, I am actively developing an AngularJS protractor test suite. The configuration file for this project is structured in the following way: exports.config = { seleniumAddress: 'http://localhost:4444/wd/hub', baseUrl: 'http: ...

Trouble with conflicting Javascript on my webpage

I am facing an issue with my web page, where the lightwindow script is not functioning properly due to a conflict with the accordion menu script. When I remove the accordion script, the lightwindow script works fine. I suspect there is a conflict between t ...

Updating the Structure of Various Objects with an Object Model Schema

Hello there, Query: Can you provide guidance on constructing a function that does the following: Accepts an object as the first argument; Takes a schema model as the second argument; The second argument is an array of objects with different models than t ...

Modify the formatting within a document without creating a separate file

Currently, I am trying to extract and replace HTML markup placeholders using a dataset in order to create a merged string. Although I have used server-side templates in the past, the client-side code now requires consistent markup for front-end purposes be ...

Enhance the current API functionality by incorporating personalized endpoints

Developing an API for multiple clients presents a unique challenge. While core endpoints like /users are common across all clients, some features may require individual customization. For instance, one client (e.g. "User A") may request a specialized endpo ...

Functions perfectly on Chrome, however, encountering issues on Internet Explorer

Why does the Navigation Bar work in Chrome but not in Internet Explorer? Any insights on this issue? The code functions properly in both Internet Explorer and Chrome when tested locally, but fails to load when inserted into my online website editor. Here ...