How To Build A Stopwatch in JavaScript
Tags: javascript, webdev, programming, tutorial
Published: 2022-06-02 23:19:16

In today's tutorial, I will show you how you can build a simple stopwatch in JavaScript using a constructor function.

A constructor function is a function that is used to create objects.

## A Refresher on Objects

An object is an unordered collection of data in key-value pairs. A single pair of this data is called a property of that object.

Here are some ways to create an object in JavaScript;

- Using the object literal syntax **{ }**
```javascript
const obj = {
    name : "simon"
}
console.log(obj);
// {name: 'simon'}
```
- Using a constructor function
```javascript
function myProfile(name){
    this.name = name;
}
const obj = new myProfile("simon");
console.log(obj);
// myProfile {name: 'simon'}
```
- Using a factory function
```javascript
function myProfile(name){
    return{
        name : name
    }
}
const obj = myProfile("simon");
console.log(obj);
// {name: 'simon'}
```

Having understood what objects are, and how you can create one, I will show you how to use a constructor function to build a stopwatch.

## LET'S BEGIN

Our Constructor function will be named StopWatch and it will have variables and methods that will start our watch, stop our watch, reset our watch and display the duration.

> A method is an object property that contains a function definition.

We will create variables inside our constructor function below, and these variables are only to be modified by methods within our function.
 
```javascript
function StopWatch(){
     let isStart = false; //start the watch
     let startCount = 0; //record time started
     let stopCount = 0; //record time stopped
     let durationCount = 0; //calculate duration
     let preDuration = 0; //calculate previous duration
}
```
Here are the variables within our constructor function;

- isStart

This variable will be used to check if our watch is started. If the value is **true**, it means that the watch is started but if the value is **false**, it means that the watch is stopped or not started.

- startCount

This variable will store the timestamp when our watch was started.

- stopCount

This variable will store the timestamp when our watch was stopped.

- durationCount

This variable stores the duration by finding the difference between startCount & stopCount.

- preDuration

This variable stores the previous duration as long as the stopwatch is not reset.

### START METHOD

Now we are about to create the first method that will be responsible for starting our watch.

This method will check:

- If the watch has already been started ie **(isStart == true)**. 

If yes, it will log a message to the console informing the user that the *watch has been started already*.

If no, it will start the watch by retrieving the timestamp from a date object and storing it to **startCount**, then it will change the value of **isStart to true**.

- If the startCount is zero (0). 

This variable holds the timestamp when our watch was started. If the value is zero (0), it will start the watch by retrieving the timestamp from a date object and storing it to **startCount**, then it will change the value of **isStart to true**.
It will then inform the user that *the watch has been started*.

- If none of the above is true

It will start the watch by retrieving the timestamp from a date object and storing it to **startCount**, then it will change the value of **isStart to true**.
It will then inform the user that the *watch has been continued.* ie resume.

```javascript
function StopWatch(){
     this.start = function(){
        //check if watch is started
        if(isStart === true){
            console.log("Stop watch is already started");
        }else if(startCount === 0){
            let dt = new Date();
            //retrieve timestamp from the date object
            startCount = dt.getTime();
            //start the watch by changing the value to true
            isStart = true;
            console.log("Stop watch has been started");
        }else{
            let dt = new Date();
            //retrieve timestamp from the date object and store
            startCount = dt.getTime();
            //start the watch by changing the value to true
            isStart = true;
            console.log("Stop watch has been continued");
        }
    };
}
```

### STOP METHOD

This method will be responsible for stopping our watch.

This method will check:

- If the watch was started. ie **(isStart == true)**

If not, it will inform the user that the *watch has not been started*. 

If yes, it will stop the watch by retrieving the timestamp from a date object and storing it to **stopCount**, then it will change the value of **isStart to false**.

It will then inform the user that the *watch has been stopped*.

```javascript
function StopWatch(){
    this.stop = function(){
        //check if stopwatch was started
        if(isStart === false){
            console.log("Stop watch is not started");
        }else{
            let dt = new Date();
            //retrieve timestamp from the date object and store
            stopCount =  dt.getTime();
            //stop the watch by changing the value to false
            isStart = false;
            console.log("Stop watch has been stopped");
        }
    };
}
```
### DURATION METHOD

This method is responsible for calculating and returning the time our watch elapsed, in seconds.

It is important to note that using the method **getTime()** to retrieve the timestamp from a date object, returns the time in **milliseconds** and we need to divide this by **1000**  to get the time in seconds.

Then we store the result to the previous duration variable **(preDuration)** so that we will keep adding it to the new duration as long as the watch is not reset.

You will also notice that I converted the time to minutes if it is greater than 60. This means that instead of showing 120 seconds, you would see 2 minutes instead. 

Most of the results will end up with decimals so it is necessary to approximate it to 1 decimal place using the method **toFixed().**

```javascript
function StopWatch(){
    this.duration = function(){
        let dur = 0;
        //divide by 1000 to get the time in seconds
        durationCount = (stopCount - startCount)/1000;
        //check if previous duration was saved
        if(preDuration === 0) {
            //save new duration
            preDuration = durationCount
        }else{
            //add previous duration to new duration
            durationCount+=preDuration;
        }
        //check if duration is greater than 60 so we can convert to minutes
        if(durationCount > 60){
            //convert to minutes
            dur = durationCount / 60;
            //return duration in minutes by approximating to 1 decimal place
            return (Number(dur.toFixed(1))+" Minutes");
        }else{
            //return duration in seconds by approximating to 1 decimal place
            return (Number(durationCount.toFixed(1)) + " Seconds");
        }
    };
}
```

### RESET METHOD

This is the last method of our stopwatch that will be used to reset the watch back to its initial state.

All it does is reset the variables declared within our constructor function to their initial state.

```javascript
function StopWatch(){
    this.reset = function(){
        durationCount = startCount = stopCount = preDuration = 0;
        isStart = false;
        return true;
    }
}
```
Here's the full code

```javascript
function StopWatch(){
    let isStart = false; //start the watch
    let startCount = 0; //record time started
    let stopCount = 0; //record time stopped
    let durationCount = 0; //calculate duration
    let preDuration = 0; //calculate previous duration
    //start method
    this.start = function(){
        //check if watch is started
        if(isStart === true){
            console.log("Stop watch is already started");
        }else if(startCount === 0){
            let dt = new Date();
            //retrieve timestamp from the date object
            startCount = dt.getTime();
            //start the watch by changing the value to true
            isStart = true;
            console.log("Stop watch has been started");
        }else{
            let dt = new Date();
            //retrieve timestamp from the date object and store
            startCount = dt.getTime();
            //start the watch by changing the value to true
            isStart = true;
            console.log("Stop watch has been continued");
        }
    };
    //stop method
    this.stop = function(){
        //check if stopwatch was started
        if(isStart === false){
            console.log("Stop watch is not started");
        }else{
            let dt = new Date();
            //retrieve timestamp from the date object and store
            stopCount =  dt.getTime();
            //stop the watch by changing the value to false
            isStart = false;
            console.log("Stop watch has been stopped");
        }
    };
    //duration
    this.duration = function(){
        let dur = 0;
        //divide by 1000 to get the time in seconds
        durationCount = (stopCount - startCount)/1000;
        //check if previous duration was saved
        if(preDuration === 0) {
            //save new duration
            preDuration = durationCount
        }else{
            //add previous duration to new duration
            durationCount+=preDuration;
        }
        //check if duration is greater than 60 so we can convert to minutes
        if(durationCount > 60){
            //convert to minutes
            dur = durationCount / 60;
            //return duration in minutes by approximating to 1 decimal place
            return (Number(dur.toFixed(1))+" Minutes");
        }else{
            //return duration in seconds by approximating to 1 decimal place
            return (Number(durationCount.toFixed(1)) + " Seconds");
        }
    };
    //reset method
    this.reset = function(){
        durationCount = startCount = stopCount = preDuration = 0;
        isStart = false;
        return true;
    };
}
```
In order to test our watch, you need to copy & paste the above constructor function into your browser's console, create an instance of the object, and test run!

Here's a list of the methods;

- **start()**: This method starts the watch or resumes a stopped watch.

- **stop()**: This method stops the watch.

- **duration()**: This method calculates and returns the time elapsed.

- **reset()**: This method resets the watch to its initial state

```javascript
//create new instance of the object
let watch = new StopWatch();
//start the watch
watch.start();
//stop the watch after 5 seconds
watch.stop();
//caculate duration
console.log(watch.duration());
```
Here's my result

![stopwatch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/37twg3qwu5frmw1l4tpv.png)
 
When I want the watch to continue from where it stopped, I would simply invoke the **start()** method without having to reset the watch.

![stopwatch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxyjfi8gtszh9izn0r46.png)

What if we made the Stopwatch run for a couple of minutes?

![stopwatch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vhn8at8jufbg55lnydeu.png) 

If you ever need to reset the watch, simply invoke the reset method **reset()** on your object instance.

You can play with your new computer-based JavaScript stopwatch and check the consistency of your results by timing your start & stop, then comparing it with your local stopwatch. Who knows? They might just be the same.

You've reached the end of my article.

## EXTRA

I recently launched a JavaScript package that validates your HTML forms using validation rules, regular expressions and form input attributes.

I will appreciate if you spared a few seconds to check out this wonderful package.

{% embed https://www.producthunt.com/posts/octavalidate %}

Thank You.

Image credit: Vecteezy.com