Building A Stopwatch With JavaScript

5 min read

It’s been a while since I have written anything about or in the realm of JavaScript. This little post is coming back to the basics of JavaScript and DOM manipulation. I will walk you through how to build a stop watch with start/pause, reset, and lap functionality built in to it.

The Skeleton and Makeup

The below code snippets are the HTML and CSS I will be using. Feel free to copy and paste them into your editor.

stopwatch.html
<div class="app">
	<div id="watch"></div>
	<div class="controls">
		<button id="start">Start</button>
		<button id="lap">Lap</button>
		<button id="reset">Reset</button>
	</div>
	<div id="laps"></div>
</div>
stopwatch.html
.app {
	align-items: center;
	background-color: darkseagreen;
	display: flex;
	flex-direction: column;
	height: 100vh;
	justify-content: center;
}
 
.controls {
	display: flex;
	justify-content: space-around;
}
 
#watch {
	align-items: center;
	border: 2px solid darkslategray;
	color: darkslategray;
	display: flex;
	font-family: 'Courier New', Courier, monospace;
	font-size: 4rem;
	height: 10vh;
	justify-content: center;
	padding: 1rem;
	text-align: center;
	width: 80%;
}
 
#laps {
	color: darkslategray;
	font-family: 'Courier New', Courier, monospace;
	font-size: 2rem;
	height: 400px;
	overflow: scroll;
	text-align: center;
	width: 80%;
}
 
button {
	background-color: darkslategray;
	border: none;
	box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
	color: white;
	font-family: 'Courier New', Courier, monospace;
	font-size: 2rem;
	height: 3rem;
	margin: 1rem;
	outline: none;
}

On to the JavaScript

So JavaScript has 2 native timing functions built in: setTimeout, setInterval.

setTimeout will run a timer one time for the set duration; where as,setInterval allows us to execute code at set intervals, so for example if we want to show a cat on the screen every 3 seconds this is the timing function we would want to use. Both timing functions return a timing id that is unique to the browser instance it is being ran in. The timer id is what we will need to clear the timer when we want to be done with it.

stopwatch.html
setTimeout(() => {
	// Code here will execute one time 5 seconds from now.
}, 5000)
 
let timer = setInterval(() => {
	// Code here will continuously execute at 3 second intervals.
}, 3000)
 
console.log(timer) // 1234, the timer id

First we will grab our elements for later use and setup some temporary variables. Let’s not forget to get something on the screen in our watch div too!

stopwatch.html
const startBtn = document.getElementById('start')
const lapBtn = document.getElementById('lap')
const resetBtn = document.getElementById('reset')
const watch = document.getElementById('watch')
const laps = document.getElementById('laps')
let RUNNING = false
let time = 0
let timer
 
watch.innerHTML = '00:00:00'

Our start button needs to handle the functionality of being both a start button and a pause button. To do that we will check to see if the state of our application is running and if it isn’t we will first set the state to running. Then we will start the stopwatch and finally toggle the text inside the button so the user knows they can pause the stopwatch as well.

stopwatch.html
startBtn.addEventListener('click', event => {
	if (!RUNNING) {
		RUNNING = true
		stopwatch()
		startBtn.innerHTML = 'Pause'
	} else {
		RUNNING = false
		stopwatch()
		startBtn.innerHTML = 'Start'
	}
})

Now we get into the meat and potatoes of our application. Our stopwatch function needs to know the state of the application so it knows when to start an interval and when to clear the current interval. Instead of using the Date.now we are making use of a simple counter that will increment on every millisecond interval. We store our timer in a temporary variable so we have access to the timer id we talked about earlier. It’s through this id we will be able to clear the interval later on when we toggle the state of our application. We make use of ES6 template strings to build out the display of the time and make use of ternary expressions for when we need to pad values with zero.

stopwatch.html
const stopwatch = () => {
	if (RUNNING) {
		timer = setInterval(() => {
			time++
			const minutes = Math.floor(time / (60 * 60))
			const seconds = Math.floor(time / 60) % 60
			const milliseconds = time % 100
 
			watch.innerHTML = `${minutes}:${seconds < 10 ? '0' + seconds : seconds}:${
				milliseconds < 10 ? '0' + milliseconds : milliseconds
			}`
		}, 10)
	} else {
		clearInterval(timer)
	}
}

Our lap button and reset button are both pretty straightforward. For our lap button we are just going to peak in on the value in our watch div at the current moment and display that out as a lap value. For our reset button we will toggle the state of the application, change our button text and clear out the timer. We will also take the time to do some cleanup and reset the display.

stopwatch.html
lapBtn.addEventListener('click', event => {
	const currentTime = watch.innerHTML
	const lap = document.createElement('p')
	lap.innerHTML = `${currentTime}`
	laps.appendChild(lap)
})
 
resetBtn.addEventListener('click', event => {
	RUNNING = false
	startBtn.innerHTML = 'Start'
	clearInterval(timer)
	watch.innerHTML = '00:00:00'
	laps.innerHTML = ''
})

And that’s it! You’ve built a stopwatch using JavaScript. Pretty straightforward albeit not necessarily intuitive if it’s your first time working with timers in JavaScript. I hope you enjoyed this post and choose to share it with your friends.

~ Cody 🚀

Related Articles


    Going to the Gopher Side

    The chaos that is the JavaScript/TypeScript ecosystem has become too much to bear in my opinion. I have become unhappy with my direction in the tech industry and in late 2023 made the decision to begin teaching myself Go and pivoting my career out of the Frontend & away from JavaScript.

    Migrating From Yarn To Pnpm

    My experience moving from classic yarn to pnpm as my package manager in JavaScript land.

Cody Brunner

Cody is a Christian, USN Veteran, Jayhawk, and an American expat living outside of Bogotá, Colombia where he works as a Senior Frontend Developer for WAO Fintech.