Skip to main content
← JavaScript

Lesson 10 of 11

Study Timer

Code that runs when nobody is touching the page. Once a second, until you stop it.

What you will learn

  • setInterval
  • clearInterval
  • Remainder with %
  • padStart
  • Stopping a second timer
  • Disabled buttons

Before you start

Rock Paper Scissors. There you kept a score in a variable between clicks. Here the page keeps counting between clicks, with nobody pressing anything.

What this page does

  1. Click Start. The clock counts down once a second.
  2. Start switches itself off while it runs. Pause switches itself on.
  3. Click 1 min and it resets to one minute, so you can watch it reach zero.

setInterval, and the number it hands back

ticking = setInterval(tick, 1000);

That asks the browser to run tick every 1000 milliseconds, which is once a second, for ever. The function that started it finishes straight away. The repeating carries on without it.

clearInterval(ticking);
ticking = null;

clearInterval takes that number and ends the job. Setting ticking back to null afterwards is what lets the rest of the file tell whether anything is running. That one variable answers the question, so nothing else has to keep track of it separately.

The two numbers

The first one is the second argument to setInterval. Change it and the clock still takes one second off per tick, so the whole timer speeds up or slows down.

1000ms

1000 is one second. Set it to 200 and press Start. The timer now runs five times too fast, because it still takes one second off every time.

20

Set this to 1 to test the end of the timer without waiting twenty minutes.

The bug this lesson exists to teach

timer.js, the first line of start()
function start() {
    if (ticking !== null) return;
    ...
}

Without that line, clicking Start twice makes two repeating jobs. Both take a second off the same number, so the clock runs at double speed. Click it five times and it runs five times too fast.

It is worse than looking wrong. The first job's number was overwritten by the second, so nothing can ever stop the first one. Pause stops one job and the other keeps going.

Every setInterval in your code needs an answer to one question: what stops this from being started twice? Here the answer is one if. That is usually all it takes.

Seconds, turned into a clock

The code counts in seconds and nothing else. Minutes and colons are for showing a person, and that happens in one place.

timer.js
function asClock(seconds) {
    const minutes = Math.floor(seconds / 60);
    const rest = seconds % 60;
    return String(minutes).padStart(2, "0") + ":" + String(rest).padStart(2, "0");
}
For 90 secondsGivesBecause
Math.floor(90 / 60)1How many whole minutes fit. floor throws away the rest
90 % 6030The remainder. What is left over
String(8).padStart(2, "0")"08"A zero in front, so the clock never reads 1:8

Keeping one unit inside the code and converting only when you show it is a habit worth building. Every sum in this file is a sum about seconds, so none of them has to think about minutes at all.

Buttons that switch themselves off

startBtn.disabled = ticking !== null || secondsLeft === 0;
pauseBtn.disabled = ticking === null;

disabled is a property you can set from code. A disabled button cannot be clicked, cannot be reached with Tab, and is not read out as pressable.

Both lines are worked out from ticking and secondsLeft. Nothing switches a button on when a click happens. show() re-reads the state and sets both every time, so the buttons cannot get out of step with what the timer is doing.

Build it step by step

Get one second to pass before you build anything around it.

  1. Make the page

    Make index.html with a <p id="clock">, and Start, Pause and Reset buttons. Put the <script> tag at the end.

  2. Prove setInterval runs

    Before anything else, write this and reload. Open the console with F12. A line appears every second, and it keeps appearing. Watch it for ten seconds, then reload the page to stop it.

    setInterval(function () {
        console.log("tick");
    }, 1000);
  3. Count down in the console

    Add secondsLeft and take one off inside the interval. Log it. You now have a countdown with no page attached to it.

  4. Put it on the page

    Write asClock and a show function. Call show() from inside the tick. The clock now moves.

  5. Add Start and Pause

    Move the setInterval into a start function and connect it to the button. Write stop with clearInterval and connect Pause. Test both.

  6. Find the double-start bug

    Click Start three times. The clock speeds up. Add if (ticking !== null) return; at the top of start and try again. This step is the lesson.

  7. Stop at zero

    In tick, when secondsLeft reaches 0, set it to 0 and call stop(). Without this the clock counts into negative numbers.

  8. Switch the buttons on and off

    Set disabled on both buttons inside show, worked out from ticking. Never set it inside a click handler.

Try these changes

Make these changes in the files beside this guide. Each one only uses what this lesson has already covered. Tick each one when it works.

Check yourself

Three quick questions on this lesson. Get them all right to finish the lesson and earn a pack.

Loading your answers

Teacher's versionHow to run this lesson, what to check, and any answers. Students do not need this.

How to teach it

  • Do step 2 on its own and leave it logging for a full ten seconds while you say nothing. Nobody is clicking and the page is still working. That silence is the idea of the lesson.
  • Let them find the double-start bug themselves. Say only "click Start a few times" and wait. A student who has made a timer they cannot stop understands why the guard exists.
  • Ask what ticking holds when nothing is running. null is the answer, and the fact that ONE variable answers both "what job" and "is anything running" is worth naming.
  • Set the tick-speed slider to 200 and ask why the timer is wrong. The interval fires five times a second and the code still takes a whole second off each time. Separating how often something runs from how much it changes is the useful idea.
  • The seconds-only rule is worth saying out loud. Every number in the file is seconds. Minutes exist in exactly one function, at the moment of showing it.