Javascript Stopwatch class

Why, the motivation

Doing a search on Google for javascript stopwatch seems to yield no usable object-oriented source code. It's all old school and tied to the HTML that is set-up on the specific page. I found a need for an isolated stopwatch in an Mac OS X widget that I was developing.



How to use, 3 steps

  1. Include the library:
    <script src="stopwatch.js"></script>
  2. Create and start the watch:
    var myWatch = new Stopwatch(); myWatch.start();
  3. Stop it and alert the elapsed time
    myWatch.stop(); alert(myWatch.toString());


Download stopwatch.js


new Stopwatch([listener, resolution])
Creates a Stopwatch object with the specified tick listener function and resolution milliseconds. See setListener() for more info. Both arguments are optional.

Stops the watch and returns the elased time as an object. See Stopwatch.getElapsed() for the return format.

Starts the watch. If a listener is attached via setListener() the listener is triggered each time resolution milliseconds has passed. The resolution can be set via watch.resolution = 1000 (one second) or via the constructor: new Stopwatch(listener, 1000).

Resets the watch to 0. If the watch is running it will continue to run from 0. Resetting does not trigger your listener function (e.g. it does not update the display of the watch). If you want to update the display, you can force a tick event by calling mywatchobject.onTick();

Restarts the watch from 0. Equivalent to calling stop(), reset() and start().

Returns the total elapsed time in an object. The attributes are: hours, minutes, seconds and milliseconds. Example:

var e = myWatch.getElapsed();
alert(e.hours + "h "+e.minutes+"m "+e.seconds+"s "+e.milliseconds+"ms has passed");

Stopwatch.setElapsed(hours, minutes, seconds)
Resets the stopwatch and elapsed time to the specific time. It does not stop the watch, so a running watch will immediately continue from the specified time. All parameters are added together. Thus, specifying more than 60 minutes or 60 seconds is allowed and will act as if adding 1 hour or 1 minute respectively. Specifying 1.5 hours is also equivalent to specifying 1 hour and 30 minutes.

Returns a human readable version of the elapsed time. Also works while running.

Sets the listener of the stopwatch. The specified listener function will be called each time Stopwatch.resolution milliseconds has passed with 1 argument referring to the stopwatch object. Typically used for updating the display of a watch. Example:

function updateClock(watch) {
    document.getElementById('watchdisplay').innerHTML = 'Time passed: '+watch.toString();

13 thoughts on “Javascript Stopwatch class”

  1. I implemented you code in a basic web site with the start, stop and reset functions, but the reset will not go back to "00:00:00:000". It does however begin again from 0 when the start button is pushed. How do I get the reset button to display the correct formatted content?

  2. Would you mind putting this code on github so that others may contribute to it?

    It would also be easier to find if it was there.

  3. Thank you for the code, great implementation. Here is a modified version of it that allows you to make it either an up or down stopwatch:

    Stopwatch = function (listener, countUp, resolution) {
    this.startTime = 0;
    this.stopTime = 0;
    this.totalElapsed = 0; // * elapsed number of ms in total
    this.initialElapsed = 0;
    this.started = false;
    this.listener = (listener != undefined ? listener : null); // * function to receive onTick events
    this.countUp = countUp;
    this.tickResolution = (resolution != undefined ? resolution : 500); // * how long between each tick in milliseconds
    this.tickInterval = null;
    this.onehour = 1000 * 60 * 60;
    this.onemin = 1000 * 60;
    this.onesec = 1000;
    Stopwatch.prototype.start = function () {
    var delegate = function (that, method) { return function () { return; }; };
    if (!this.started) {
    this.startTime = new Date().getTime();
    this.stopTime = 0;
    this.started = true;
    this.tickInterval = setInterval(delegate(this, this.onTick), this.tickResolution);
    Stopwatch.prototype.stop = function () {
    if (this.started) {
    this.stopTime = new Date().getTime();
    this.started = false;
    var elapsed = this.stopTime – this.startTime;
    this.totalElapsed += elapsed;
    if (this.tickInterval != null) {
    return this.getElapsed();
    Stopwatch.prototype.reset = function () {
    this.totalElapsed = 0;
    this.startTime = new Date().getTime();
    this.stopTime = this.startTime;
    if (!this.countUp) {
    this.totalElapsed = this.initialElapsed;
    if (this.tickInterval != null) {
    var delegate = function (that, method) { return function () { return; }; };
    this.tickInterval = setInterval(delegate(this, this.onTick), this.tickResolution);
    Stopwatch.prototype.restart = function () {
    Stopwatch.prototype.getElapsed = function () {
    var elapsed = 0;
    if (this.started) {
    elapsed = new Date().getTime() – this.startTime;
    elapsed += this.totalElapsed;
    if (!this.countUp) {
    elapsed = Math.max(2 * this.initialElapsed – elapsed, 0);

    var hours = parseInt(elapsed / this.onehour);
    elapsed %= this.onehour;
    var mins = parseInt(elapsed / this.onemin);
    elapsed %= this.onemin;
    var secs = parseInt(elapsed / this.onesec);
    var ms = elapsed % this.onesec;

    return {
    hours: hours,
    minutes: mins,
    seconds: secs,
    milliseconds: ms
    Stopwatch.prototype.setElapsed = function (hours, mins, secs) {
    this.totalElapsed = 0;
    this.startTime = new Date().getTime();
    this.stopTime = this.startTime;
    this.totalElapsed += hours * this.onehour;
    this.totalElapsed += mins * this.onemin;
    this.totalElapsed += this.countUp ? secs * this.onesec : (secs + 1) * this.onesec – 1;
    this.totalElapsed = Math.max(this.totalElapsed, 0);
    this.initialElapsed = this.totalElapsed;
    if (this.tickInterval != null) {
    var delegate = function (that, method) { return function () { return; }; };
    this.tickInterval = setInterval(delegate(this, this.onTick), this.tickResolution);
    Stopwatch.prototype.toString = function () {
    var zpad = function (no, digits) {
    no = no.toString();
    while (no.length < digits)
    no = '0' + no;
    return no;
    var e = this.getElapsed();
    return zpad(e.minutes, 2) + ":" + zpad(e.seconds, 2);
    Stopwatch.prototype.setListener = function (listener) {
    this.listener = listener;
    // * triggered every ms
    Stopwatch.prototype.onTick = function () {
    if (this.listener != null) {

  4. Does not work when trying to launch on body load. There is no example of the full code? Can it start when a popup launches instead of using a button?

  5. Sam: Yes, it can. This very page has an example of the full code. Use View source and check it out. The button calls "w.start()" where "w" is the variable set to a new instance of the Stopwatch class ("var w = new Stopwatch();"). In your code that launches your popup, you can place the .start() call and it will start. You will still need to set it up properly. It would be something like:
    var w = new Stopwatch();''); w.start();

  6. Hi,
    I would like to use this coude but under one button (shwitch) like start, stop, resume. I want only stop the watch (forever) under other button.

  7. Hi All
    This is really a nice stopwatch code. I am fairly new to javascript. I am trying to put the Stopwatch.setElapsed(hours, minutes, seconds) method to get a custom start time through a text box but not yet successful. can anybody help me on these with sample html and javascript codes? Thanks in advance

  8. Hi Shahab
    You'll need to be a bit more specific. setElapsed will only set how much time has passed. If you want to output the value into a textbox, you'll need to use the setListener() function as described in text.
    so given that myWatch refers to the stopwatch instance:
    myWatch.setListener(function(watch) {
    document.getElementById('mytextboxid').value = 'Time passed: '+watch.toString();

Leave a Reply

Your email address will not be published. Required fields are marked *