Building A Stopwatch With JavaScript
5 min readIt’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.
<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>
.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.
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!
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.
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.
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.
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.
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.
What I Learned Re-learning GraphQL
Some items I picked up when re-learning Facebook's GraphQL.
Higher Order Components
A short piece on higher-order-functions in React.
Deploying With Now
How to deploy with Zeit's Now platform.
Cody is a Christian, USN Veteran, Jayhawk, and an American expat living outside of Bogotá, Colombia. He is currently looking for new opportunities in the tech industry.