Search code examples
javascripttimerstopwatchmilliseconds

Have A Timer That Counts both Seconds and Milliseconds


Hello everyone i have what should be a simple problem. Basically I need a timer that counts both the seconds and milliseconds. I already have constructed a timer which counts down in seconds but ran into some trouble when it came to adding the millisecond function. When I tried to add a second timer and simply place it beside my first one, it interfered with my first timer. I haven't been doing Java script for long so I have no clue what to do next.

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>

    <body id="body">

        <div id="timer" style="font-family:helvetica; font-size:100px; text-align:center;">24 secs</div>
        <script>
            var count = 24,
                counter = setInterval(timer, 1000),
                running = true;

            function timer() {
                count -= 1;
                if (count <= 0) {
                    clearInterval(counter);
                }
                document.getElementById("timer").innerHTML = count + " secs";
            }
            window.addEventListener("keydown", function(e) {
                switch (e.keyCode) {
                    case 32: // PLAY
                        running ? clearInterval(counter) : counter = setInterval(timer, 1000);
                        running = !running;
                        break;
                    case 82: // RESET
                        clearInterval(counter);
                        document.getElementById("timer").innerHTML = 24 + " secs";
                        count = 24;
                        running = false;
                }
            });
        </script>
    </body>

</html>  

Solution

  • The question is slightly less trivial than it appears on first glance.

    You can't truly display every millisecond because the browser can't handle it.

    If support for old browsers (say ~pre 2012) isn't an issue I would use something like this:

    var count = 24000,
      running = true,
      secondsNode = document.getElementById("seconds"),
      millisecondsNode = document.getElementById("milliseconds"),
      mOld,
      mNew;
    
    function draw() {
      if (count > 0 && running) {
        requestAnimationFrame(draw);
        mNew = new Date().getTime();
        count = count - mNew + mOld;
        count = count >= 0 ? count : 0;
        mOld = mNew;
        secondsNode.innerHTML = Math.floor(count / 1000);
        millisecondsNode.innerHTML = count % 1000;
      }
    }
    mOld = new Date().getTime();
    draw();
    
    window.addEventListener("keydown", function(e) {
      switch (e.keyCode) {
        case 32: // PLAY
          if (running) {
            running = false;
          } else {
            running = true;
            mOld = new Date().getTime();
            draw();
          }
          break;
        case 82: // RESET
          count = 24000;
          secondsNode.innerHTML = 24;
          millisecondsNode.innerHTML = 0;
          running = false;
      }
    });
    p {
      font-family: helvetica;
      font-size: 100px;
      text-align: center;
    }
    <body id="body">
    
      <p><span id="seconds">24</span> secs and <span id="milliseconds">000</span> milliseconds</p>
    
    </body>

    The core here is the method requestAnimationFrame(...);. Simply put it is a native JS method that executes the function supplied as a parameter once the browser is ready to "draw" a new frame.

    Since we don't know how much time has passed since the last execution we need use the current datetime in relation to the datetime of our methods last execution.


    On a sidenote:

    Regularly needed nodes should be saved to a variable instead of being "calculated" each time they are needed. (secondsNode = document.getElementById(...) saves resources because the document only hase to be traversed once.)

    Here some further reading on requestAnimationFrame.

    And the above code as a Fiddle.