skip to Main Content

I am making a Rubik’s cube scrambler and timer website, and it works fine so far. I have a scrambler, a timer, and a list of times (everything still in development). The timer works fine but the only problem is that when it updates to the next minute and resets the second count, for example it is at 0:59 then it should go to 1:00 but instead it goes to 1:60 before going to 1:01. Any help would be appreciated!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Speedcubing Timer</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <style>
        * {
            padding:20px;
        }
        #times-list-template {
            height:20vh;
            max-height: 20vh;
            overflow-y: auto;
        }
        .hidden {
            display:none;
        }
        #timer-info-box {
            position:fixed;
            bottom:90vh;
            height:10vh;
            background-color:gray;
            padding:5px;
        }
    </style>
</head>
<body>
<h1>CubeTimer v1.0</h1>
    <div class="row">
        <div class="col-xs-5">
            <h1>Scramble</h1>
            <h3 id="scramble"></h1>
        </div>
        <div class="col-xs-5">
            <h1>Timer<i class="fa-solid fa-circle-info" id="timer-info"></i></h1>
            <h3 id="timer"><span id="minutes"></span>:<span id="seconds"></span></h3>
            <button onclick="list_time()" class="btn btn-primary">Save to list</button>
        </div>
        <div class="col-xs-5">
            <div id="timer-info-box" class="hidden"><p>Press the spacebar to start the timer!</p></div>
            <h1>Times</h1>
            <div id="times-list"><ol id="times-list-template"></ol></div>
        </div>
    </div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://kit.fontawesome.com/c63b6d877c.js" crossorigin="anonymous"></script>
<script>
let scramble = Math.floor(Math.random() * 12);
function scramble_translator() {
    let scramble = Math.floor(Math.random() * 12);
    if (scramble <= 1) {
        scramble = 'R '
    } else if (scramble > 1 && scramble <= 2) {
        scramble = 'R' '
    } else if (scramble > 2 && scramble <= 3) {
        scramble = 'U '
    } else if (scramble > 3 && scramble <= 4) {
        scramble = 'U' '
    } else if (scramble > 4 && scramble <= 5) {
        scramble = 'L '
    } else if (scramble > 5 && scramble <= 6) {
        scramble = 'L' '
    } else if (scramble > 6 && scramble <= 7) {
        scramble = 'F '
    } else if (scramble > 7 && scramble <= 8) {
        scramble = 'F' '
    } else if (scramble > 8 && scramble <= 9) {
        scramble = 'D '
    } else if (scramble > 9 && scramble <= 10) {
        scramble = 'D' '
    } else if (scramble > 10 && scramble <= 11) {
        scramble = 'B '
    } else if (scramble > 11 && scramble <= 12) {
        scramble = 'B' '
    }

    document.getElementById('scramble').innerHTML += scramble;
}
for (let i = 0; i < 20; i++) {
    scramble_translator();
}
document.getElementById('seconds').innerHTML = 0;
document.getElementById('minutes').innerHTML = 0;
let intervalId;
let minutes = 0;
let seconds = 0;

function timer() {
    document.addEventListener('keydown', function(event) {
        if (event.key === ' ') {
            if (seconds === 0) {
                intervalId = setInterval(function() {
                    seconds++;
                    document.getElementById('seconds').innerHTML = seconds;

                    if (seconds > 59 || seconds > 60 && seconds > 119 || seconds > 120 && seconds > 179 || seconds > 180 && seconds > 239) {
                        minutes++;
                        document.getElementById('minutes').innerHTML = minutes;
                        seconds = 0;
                    }
                }, 1000);
            } else {
                clearInterval(intervalId);
            }
        }
    });
}
timer();

function list_time() {
    let new_time = $('#timer').html();
    $('<div>').html(new_time).appendTo('#times-list-template');
    document.getElementById('scramble').innerHTML = '';
    for (let i = 0; i < 20; i++) {
        scramble_translator();
    }
}
document.getElementById('timer-info').addEventListener('mouseover', (event) => {
    document.getElementById('timer-info-box').classList.remove('hidden');
});
document.getElementById('timer-info').addEventListener('mouseout', (event) => {
    document.getElementById('timer-info-box').classList.add('hidden');
});

document.addEventListener('keydown', (event) => {
    if (event.key === 'Enter') {
        list_time();
    }
});
</script>
</body>
</html>

3

Answers


  1. There are some problems in your attempt, including:

    • The condition if (seconds > 59 || seconds > 60 ... makes little sense. When seconds > 59 is true, the rest of the condition will not even be evaluated, since that is enough to have the whole expression evaluate to true. Instead, you would benefit from using arithmetic: there is a remainder operator in JavaScript, so you can easily extract the number of seconds (within a single minute) from a total number of seconds, and from that derive the number of minutes. So I would suggest to just keep increasing seconds and then derive the two components from that with simple arithmetic.

    • if (seconds === 0) is not a good condition to see if the timer has already started. Remember that the number of seconds is regularly set back to 0 (every time a minute passes). Instead, test whether the intervalId has been set, and clear it whenever you clear the timer.

    • The number of seconds are traditionally displayed with 2 digits, so you should prefix with an extra "0" when the number of seconds is less than 10.

    Here is how it could work. I removed all Cube related stuff, and CSS, as it is not related to your question:

    document.getElementById('seconds').innerHTML = "00";
    document.getElementById('minutes').innerHTML = 0;
    
    let intervalId;
    let seconds = 0;
    
    document.addEventListener('keydown', function(event) {
        // -1 is used as a value to indicate that the timer was stopped
        if (event.key !== ' ' || intervalId === -1) return;
        if (intervalId !== undefined) {
            clearInterval(intervalId);
            intervalId = -1; // Don't allow time to restart
        } else {
            intervalId = setInterval(function() {
                seconds++;
                const secondsOnly = seconds % 60;
                const minutes = (seconds - secondsOnly) / 60;
                document.getElementById('seconds').innerHTML = ("" + secondsOnly).padStart(2, "0");
                document.getElementById('minutes').innerHTML = minutes;
            }, 1000);
        }
    });
    <span id="minutes"></span>:<span id="seconds"></span>
    <p>Press the spacebar to start/stop the timer!</p>

    Note that using setInterval is not a guarantee for having a precise time measurement. See How to create an accurate timer in javascript? for what you need to do to get a more accurate timing.

    Login or Signup to reply.
  2. checkout this simple script that I just uploaded to github.
    let me know if it’s helped you or not.

    countDown timer link

    Login or Signup to reply.
  3. Setting up a flow control statement like this:

    if (seconds > 59 || seconds > 60 && seconds > 119 || seconds > 120 && seconds > 179 || seconds > 180 && seconds > 239) {...
    

    doesn’t work the way you’d expect it. Once seconds is 60 or more, the first condition will always returns true for every second that passes after the 59th second. There are many other syntactical errors and code smells that keep it from functioning, so instead of going through each one, we’ll focus on a working solution instead. The following example has details commented within.

    // Reference <time>
    const timer = document.getElementById("timer");
    // Reference <form>
    const main = document.forms.main;
    // Reference <fieldset> and all <input>
    const fc = main.elements;
    
    /**
     * Variables declared within a function will be destroyed when
     * the function ends. So declare any variables that needs to 
     * maintain their value outside of any function.
     * Define "tick" outside of any function because it needs to
     * be accessed by multiple functions.
     */
    let time = 0;
    let seconds = 0;
    let tick = null;
    
    /**
     * This function is called every 1000ms (1sec) by "tick"'s setInterval().
     */
    const clock = () => {
      // Declare "minutes" and "mmss", define "sec" as "seconds"
      let minutes, mmss, sec = seconds;
      // Increment "second" by 1.
      seconds++;
      // "minutes" is 1 for every 60 "seconds"
      minutes = Math.floor(seconds / 60);
      /* When more than a minute has passed, "sec" is the remaining seconds 
         (less than 60) */
      if (minutes > 0) {
        sec = Math.floor(seconds % 60);
      }
      // This string is assigned to an element to display.
      mmss = `${String(minutes).padStart(2, "0")}:${String(sec).padStart(2, "0")}`;
      timer.datetime = mmss;
      timer.textContent = timer.datetime;
    };
    
    const start = e => {
      e.preventDefault();
      fc.set.disabled = true;
      time = Number(fc.set.value);
      /* Redefine "tick" with setInterval() 
         call the clock() function
         set a condition when the predetermined time is reached */
      tick = setInterval(() => {
        clock();
        if (seconds >= time + 1) {
          clearInterval(tick);
          if (seconds === 3601) {
            timer.textContent = "1 HOUR";
          }
        }
      }, 1000);
    };
    
    const clear = e => {
      clearInterval(tick);
      fc.set.disabled = false;
      time = 0;
      seconds = 0;
      timer.datetime = "00:00";
      timer.textContent = "00:00";
    };
    
    const stop = e => clearInterval(tick);
    
    main.onsubmit = start;
    main.onreset = clear;
    fc.stop.onclick = stop;
    :root {
      font: 2ch/1.15 "Segoe UI"
    }
    
    fieldset {
      display: flex;
      flex-flow: column nowrap;
      align-items: center;
      width: max-content;
      margin: 0 auto;
    }
    
    legend {
      font-weight: 700;
      font-size: 1.25rem;
    }
    
    input {
      height: 2rem;
      font: inherit
    }
    
    [name="button"] {
      cursor: pointer;
    }
    
    .ui {
      display: flex;
      align-items: center;
      margin-top: 0.75rem;
    }
    
    #set,
    #timer {
      font-family: Consolas
    }
    
    #set {
      width: 3.5rem;
      height: 1.8rem;
      padding: 0;
      text-align: right;
    }
    
    #timer {
      display: block;
      font-size: 3rem;
    }
    <form id="main">
      <fieldset>
        <legend>Hour Timer</legend>
        <label for="set">Set number of seconds (max 3600)</label>
        <label class="ui">
          <input id="set" type="number" min="1" max="3600" value="1" required>
          <input name="button" type="submit" value="Start">
          <input id="stop" name="button" type="button" value="Stop">
          <input name="button" type="reset" value="Clear"></label>
        <time id="timer" datetime="00:00">00:00</time>
      </fieldset>
    </form>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search