41
if(stopwatch >= track[song].duration)

track[song].duration finds the duration of a soundcloud track.

I am looking to create a stopwatch function that starts counting milliseconds when you click on the swap ID stopwatch so that when the function has been "clicked" for a certain amount of time the if function will do something. In my case replace an image. And also that the function will reset it itself when clicked again.

so like stopwatch = current time - clicked time How can I set up the clicked time

current time = new Date().getTime(); ? And is this in milliseconds?

$('#swap').click(function()...
JAL
  • 41,701
  • 23
  • 172
  • 300
maxisme
  • 3,974
  • 9
  • 47
  • 97
  • Timers in javascript are usually setup by creating Date objects at particular moments. You can then subtract Dates to get the difference in milliseconds. But it seems you know that. What seems to be missing is the events that you want to create Date objects for. – RobG Dec 02 '13 at 00:23

7 Answers7

99

You'll see the demo code is just a start/stop/reset millisecond counter. If you want to do fanciful formatting on the time, that's completely up to you. This should be more than enough to get you started.

This was a fun little project to work on. Here's how I'd approach it

var Stopwatch = function(elem, options) {

  var timer = createTimer(),
    startButton = createButton("start", start),
    stopButton = createButton("stop", stop),
    resetButton = createButton("reset", reset),
    offset,
    clock,
    interval;

  // default options
  options = options || {};
  options.delay = options.delay || 1;

  // append elements     
  elem.appendChild(timer);
  elem.appendChild(startButton);
  elem.appendChild(stopButton);
  elem.appendChild(resetButton);

  // initialize
  reset();

  // private functions
  function createTimer() {
    return document.createElement("span");
  }

  function createButton(action, handler) {
    var a = document.createElement("a");
    a.href = "#" + action;
    a.innerHTML = action;
    a.addEventListener("click", function(event) {
      handler();
      event.preventDefault();
    });
    return a;
  }

  function start() {
    if (!interval) {
      offset = Date.now();
      interval = setInterval(update, options.delay);
    }
  }

  function stop() {
    if (interval) {
      clearInterval(interval);
      interval = null;
    }
  }

  function reset() {
    clock = 0;
    render(0);
  }

  function update() {
    clock += delta();
    render();
  }

  function render() {
    timer.innerHTML = clock / 1000;
  }

  function delta() {
    var now = Date.now(),
      d = now - offset;

    offset = now;
    return d;
  }

  // public API
  this.start = start;
  this.stop = stop;
  this.reset = reset;
};


// basic examples
var elems = document.getElementsByClassName("basic");

for (var i = 0, len = elems.length; i < len; i++) {
  new Stopwatch(elems[i]);
}


// programmatic examples
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();

var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {
  delay: 100
});
bTimer.start();

var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {
  delay: 456
});
cTimer.start();

var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {
  delay: 1000
});
dTimer.start();
.stopwatch {
  display: inline-block;
  background-color: white;
  border: 1px solid #eee;
  padding: 5px;
  margin: 5px;
}

.stopwatch span {
  font-weight: bold;
  display: block;
}

.stopwatch a {
  padding-right: 5px;
  text-decoration: none;
}
<h2>Basic example; update every 1 ms</h2>

<p>click <code>start</code> to start a stopwatch</p>

<pre>
var elems = document.getElementsByClassName("basic");
  
for (var i=0, len=elems.length; i&lt;len; i++) {
  new Stopwatch(elems[i]);
}
</pre>
<div class="basic stopwatch"></div>
<div class="basic stopwatch"></div>

<hr>
<h2>Programmatic example</h2>

<p><strong>Note:</strong> despite the varying <code>delay</code> settings, each stopwatch displays the correct time (in seconds)</p>

<pre>
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();
</pre>
<div class="stopwatch" id="a-timer"></div>1 ms<br>

<pre>
var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {delay: 100});
bTimer.start();
</pre>
<div class="stopwatch" id="b-timer"></div>100 ms<br>

<pre>
var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {delay: 456});
cTimer.start();
</pre>
<div class="stopwatch" id="c-timer"></div>456 ms<br>

<pre>
var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {delay: 1000});
dTimer.start();
</pre>
<div class="stopwatch" id="d-timer"></div>1000 ms<br>

Get some basic HTML wrappers for it

<!-- create 3 stopwatches -->
<div class="stopwatch"></div>
<div class="stopwatch"></div>
<div class="stopwatch"></div>

Usage is dead simple from there

var elems = document.getElementsByClassName("stopwatch");

for (var i=0, len=elems.length; i<len; i++) {
    new Stopwatch(elems[i]);
}

As a bonus, you get a programmable API for the timers as well. Here's a usage example

var elem = document.getElementById("my-stopwatch");
var timer = new Stopwatch(elem, {delay: 10});

// start the timer
timer.start();

// stop the timer
timer.stop();

// reset the timer
timer.reset();

jQuery plugin

As for the jQuery portion, once you have nice code composition as above, writing a jQuery plugin is easy mode

(function($) {
    var Stopwatch = function(elem, options) {
    // code from above...
    };

    $.fn.stopwatch = function(options) {
    return this.each(function(idx, elem) {
        new Stopwatch(elem, options);
    });
    };
})(jQuery);

jQuery plugin usage:

// all elements with class .stopwatch; default delay (1 ms)
$(".stopwatch").stopwatch();

// a specific element with id #my-stopwatch; custom delay (10 ms)
$("#my-stopwatch").stopwatch({delay: 10});

jsbin.com demo

Teocci
  • 7,189
  • 1
  • 50
  • 48
maček
  • 76,434
  • 37
  • 167
  • 198
  • In my particular case, I call timer.start() and it's working fine, but then when I have to use timer.stop(), the 'timer' variable is going out of scope, what should I do? –  Jan 18 '14 at 09:58
  • Thanks for your interest. Anyhow, I resolved the problem few hours back. –  Jan 19 '14 at 06:18
  • That's a very good piece of code! However, there is one gotcha. If the system time gets changed while the timer is on, the timer's value will jump. How would you address this issue? – Tigran Apr 22 '16 at 07:36
  • 6
    @Tigran the timer will also stop working if the user restarts their computer or refreshes their browser tab. These are not "gotchas". I would not write code to address these "issues" unless there was some sort of justified extraneous requirement. – maček Apr 22 '16 at 11:04
  • @maček, I was asking because I use timers in my application, and am concerned about this behavior. I thought, maybe you have any idea as of how to solve this (which could, of course, be used by other ppl too). – Tigran Apr 22 '16 at 13:08
  • @Tigran I suggest you create a new question for your specific issue. You're more likely to get better answers that way. – maček Apr 25 '16 at 14:51
  • Thanks, @maček, maybe I will. – Tigran Apr 26 '16 at 12:53
  • 1
    I'd love to see this converted into ES6 using native classes and such. @maček any interest? – Justin Nov 04 '16 at 09:40
38

Two native solutions

  • performance.now --> Call to ... took 6.414999981643632 milliseconds.
  • console.time --> Call to ... took 5.815 milliseconds

The difference between both is precision.

For usage and explanation read on.



Performance.now (For microsecond precision use)

    var t0 = performance.now();
    doSomething();
    var t1 = performance.now();

    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
            
    function doSomething(){    
       for(i=0;i<1000000;i++){var x = i*i;}
    }

performance.now

Unlike other timing data available to JavaScript (for example Date.now), the timestamps returned by Performance.now() are not limited to one-millisecond resolution. Instead, they represent times as floating-point numbers with up to microsecond precision.

Also unlike Date.now(), the values returned by Performance.now() always increase at a constant rate, independent of the system clock (which might be adjusted manually or skewed by software like NTP). Otherwise, performance.timing.navigationStart + performance.now() will be approximately equal to Date.now().



console.time

Example: (timeEnd wrapped in setTimeout for simulation)

console.time('Search page');
  doSomething();
console.timeEnd('Search page');
 

 function doSomething(){    
       for(i=0;i<1000000;i++){var x = i*i;}
 }

You can change the Timer-Name for different operations.

Legends
  • 21,202
  • 16
  • 97
  • 123
  • Worth mentioning `performance.now` will [blow up IE 9](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now#browser_compatibility), if that's still important to your app and you're not transpiling. – ruffin Mar 16 '21 at 16:10
14

A simple and easy clock for you and don't forget me ;)

var x;
var startstop = 0;

function startStop() { /* Toggle StartStop */

  startstop = startstop + 1;

  if (startstop === 1) {
    start();
    document.getElementById("start").innerHTML = "Stop";
  } else if (startstop === 2) {
    document.getElementById("start").innerHTML = "Start";
    startstop = 0;
    stop();
  }

}


function start() {
  x = setInterval(timer, 10);
} /* Start */

function stop() {
  clearInterval(x);
} /* Stop */

var milisec = 0;
var sec = 0; /* holds incrementing value */
var min = 0;
var hour = 0;

/* Contains and outputs returned value of  function checkTime */

var miliSecOut = 0;
var secOut = 0;
var minOut = 0;
var hourOut = 0;

/* Output variable End */


function timer() {
  /* Main Timer */


  miliSecOut = checkTime(milisec);
  secOut = checkTime(sec);
  minOut = checkTime(min);
  hourOut = checkTime(hour);

  milisec = ++milisec;

  if (milisec === 100) {
    milisec = 0;
    sec = ++sec;
  }

  if (sec == 60) {
    min = ++min;
    sec = 0;
  }

  if (min == 60) {
    min = 0;
    hour = ++hour;

  }


  document.getElementById("milisec").innerHTML = miliSecOut;
  document.getElementById("sec").innerHTML = secOut;
  document.getElementById("min").innerHTML = minOut;
  document.getElementById("hour").innerHTML = hourOut;

}


/* Adds 0 when value is <10 */


function checkTime(i) {
  if (i < 10) {
    i = "0" + i;
  }
  return i;
}

function reset() {


  /*Reset*/

  milisec = 0;
  sec = 0;
  min = 0
  hour = 0;

  document.getElementById("milisec").innerHTML = "00";
  document.getElementById("sec").innerHTML = "00";
  document.getElementById("min").innerHTML = "00";
  document.getElementById("hour").innerHTML = "00";

}
<h1>
  <span id="hour">00</span> :
  <span id="min">00</span> :
  <span id="sec">00</span> :
  <span id="milisec">00</span>
</h1>

<button onclick="startStop()" id="start">Start</button>
<button onclick="reset()">Reset</button>
frobinsonj
  • 1,109
  • 9
  • 21
Subha Jeet Sikdar
  • 446
  • 1
  • 4
  • 15
  • Why are centiseconds called milisec? And this become completely inaccurate if the tab loses focus. – gre_gor Jul 26 '22 at 20:11
4

This is my simple take on this question, I hope it helps someone out oneday, somewhere...

let output = document.getElementById('stopwatch');
let ms = 0;
let sec = 0;
let min = 0;

function timer() {
    ms++;
    if(ms >= 100){
        sec++
        ms = 0
    }
    if(sec === 60){
        min++
        sec = 0
    }
    if(min === 60){
        ms, sec, min = 0;
    }

    //Doing some string interpolation
    let milli = ms < 10 ? `0`+ ms : ms;
    let seconds = sec < 10 ? `0`+ sec : sec;
    let minute = min < 10 ? `0` + min : min;

    let timer= `${minute}:${seconds}:${milli}`;
    output.innerHTML =timer;
};
//Start timer
function start(){
 time = setInterval(timer,10);
}
//stop timer
function stop(){
    clearInterval(time)
}
//reset timer
function reset(){
    ms = 0;
    sec = 0;
    min = 0;

    output.innerHTML = `00:00:00`
}
const startBtn = document.getElementById('startBtn');
const stopBtn =  document.getElementById('stopBtn');
const resetBtn = document.getElementById('resetBtn');

startBtn.addEventListener('click',start,false);
stopBtn.addEventListener('click',stop,false);
resetBtn.addEventListener('click',reset,false);
    <p class="stopwatch" id="stopwatch">
        <!-- stopwatch goes here -->
    </p>
    <button class="btn-start" id="startBtn">Start</button>
    <button class="btn-stop" id="stopBtn">Stop</button>
    <button class="btn-reset" id="resetBtn">Reset</button>
Dr Gaud
  • 97
  • 3
  • 1
    It's interresting, that the counter runs slower than "real" time. If you compare the seconds, you can see that it's slower. Maybe because of the if-statements that need some time to check and then it's slower (even if its only a few milliseconds, it sums up)? – Hollul Sep 22 '21 at 08:42
  • This become completely inaccurate if the tab loses focus. – gre_gor Jul 26 '22 at 20:13
2

Solution by Mosh Hamedani

Creating a StopWatch function constructor.

Define 4 local variables

  1. startTime
  2. endTime
  3. isRunning
  4. duration set to 0

Next create 3 methods

  1. start
  2. stop
  3. reset

start method

  • check if isRunning is true if so throw an error that start cannot be called twice.
  • set isRunning to true
  • assign the current Date object to startTime.

stop method

  • check if isRunning is false if so throw an error that stop cannot be called twice.
  • set isRunning to false
  • assign the current Date object to endTime.
  • calculate the seconds by endTime and startTime Date object
  • increment duration with seconds

reset method:

  • reset all the local variables.

Read-only property

if you want to access the duration local variable you need to define a property using Object.defineProperty. It's useful when you want to create a read-only property.

Object.defineProperty takes 3 parameters

  • the object which to define a property (in this case the current object (this))
  • the name of the property
  • the value of the key property.

  • We want to create a Read-only property so we pass an object as a value. The object contain a get method that return the duration local variable. in this way we cannot change the property only get it.

The trick is to use Date() object to calculate the time.

Reference the code below

function StopWatch() {


let startTime,
    endTime,
    isRunning,
    duration = 0;

  this.start = function () {
    if (isRunning) throw new Error("StopWatch has already been started.");

    isRunning = true;

    startTime = new Date();
  };



this.stop = function () {
    if (!isRunning) throw new Error("StopWatch has already been stop.");

    isRunning = false;

    endTime = new Date();

    const seconds = (endTime.getTime() - startTime.getTime()) / 1000;
    duration += seconds;
  };

  this.reset = function () {
    duration = 0;
    startTime = null;
    endTime = null;
    isRunning = false;
  };

  Object.defineProperty(this, "duration", {
    get: function () {
      return duration;
    },
  });
}

const sw = new StopWatch();
Orel Hassid
  • 301
  • 5
  • 4
1
function StopWatch() {
    let startTime, endTime, running, duration = 0
    
    this.start = () => {
        if (running) console.log('its already running')
        else {
            running = true
            startTime = Date.now()
        }
    }

    this.stop = () => {
        if (!running) console.log('its not running!')
        else {
            running = false
            endTime = Date.now()

            const seconds = (endTime - startTime) / 1000
            duration += seconds
        }
    }

    this.restart = () => {
        startTime = endTime = null
        running = false
        duration = 0
    }
    
    Object.defineProperty(this, 'duration', {
        get: () => duration.toFixed(2)
    })

}

const sw =  new StopWatch()

sw.start()
sw.stop()

sw.duration
Ahmed Boutaraa
  • 1,908
  • 1
  • 12
  • 10
0

well after a few modification of the code provided by mace,i ended up building a stopwatch. https://codepen.io/truestbyheart/pen/EGELmv

  <!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" />
    <title>Stopwatch</title>
    <style>
    #center {
     margin: 30%  30%;
     font-family: tahoma;
     }
    .stopwatch {
         border:1px solid #000;
         background-color: #eee;
         text-align: center;
         width:656px;
         height: 230px;
         overflow: hidden;
     }
     .stopwatch span{
         display: block;
         font-size: 100px;
     }
     .stopwatch p{
         display: inline-block;
         font-size: 40px;
     }
     .stopwatch a{
       font-size:45px;
     }
     a:link,
     a:visited{
         color :#000;
         text-decoration: none;
         padding: 12px 14px;
         border: 1px solid #000;
     }
    </style>
  </head>
  <body>
      <div id="center">
            <div class="timer stopwatch"></div>
      </div>

    <script>
      const Stopwatch = function(elem, options) {
        let timer = createTimer(),
          startButton = createButton("start", start),
          stopButton = createButton("stop", stop),
          resetButton = createButton("reset", reset),
          offset,
          clock,
          interval,
          hrs = 0,
          min = 0;

        // default options
        options = options || {};
        options.delay = options.delay || 1;

        // append elements
        elem.appendChild(timer);
        elem.appendChild(startButton);
        elem.appendChild(stopButton);
        elem.appendChild(resetButton);

        // initialize
        reset();

        // private functions
        function createTimer() {
          return document.createElement("span");
        }

        function createButton(action, handler) {
          if (action !== "reset") {
            let a = document.createElement("a");
            a.href = "#" + action;
            a.innerHTML = action;
            a.addEventListener("click", function(event) {
              handler();
              event.preventDefault();
            });
            return a;
          } else if (action === "reset") {
            let a = document.createElement("a");
            a.href = "#" + action;
            a.innerHTML = action;
            a.addEventListener("click", function(event) {
              clean();
              event.preventDefault();
            });
            return a;
          }
        }

        function start() {
          if (!interval) {
            offset = Date.now();
            interval = setInterval(update, options.delay);
          }
        }

        function stop() {
          if (interval) {
            clearInterval(interval);
            interval = null;
          }
        }

        function reset() {
          clock = 0;
          render(0);
        }

        function clean() {
          min = 0;
          hrs = 0;
          clock = 0;
          render(0);
        }

        function update() {
          clock += delta();
          render();
        }

        function render() {
          if (Math.floor(clock / 1000) === 60) {
            min++;
            reset();
            if (min === 60) {
              min = 0;
              hrs++;
            }
          }
          timer.innerHTML =
            hrs + "<p>hrs</p>" + min + "<p>min</p>" + Math.floor(clock / 1000)+ "<p>sec</p>";
        }

        function delta() {
          var now = Date.now(),
            d = now - offset;

          offset = now;
          return d;
        }
      };

      // Initiating the Stopwatch
      var elems = document.getElementsByClassName("timer");

      for (var i = 0, len = elems.length; i < len; i++) {
        new Stopwatch(elems[i]);
      }
    </script>
  </body>
</html>
  • The seconds go over 60 many times, some calculation is taking more time and in that case first 'if' condition in render method never gets satisfied. – Dharmesh Tailor Sep 19 '22 at 19:32