初級 JavaScript タイマー Date

ストップウォッチを作る

スタート・ストップ・リセットの3つのボタンで動く、1/1000秒まで表示するストップウォッチです。時間の計り方と、くり返し処理の止め方を学びます。

このサンプルは2017〜2018年ごろに作成したものです。PHPのバージョンが上がり、そのままでは動かなくなっていたため、2026年に PHP 8.3 で動くように書き直し、解説も新しくしました。

DEMO 実際に動かしてみよう
00:00.000

解説コードを読んでみよう

このサンプルでわかること

  • Date.now() で今の時刻(ミリ秒)を取り、経過時間を計算する方法
  • setTimeout をくり返して画面を更新し続ける方法と、clearTimeout で止める方法
  • 「動いているかどうか」を変数で管理して、ボタンの二度押しを防ぐ方法

使っているファイル

  • index.phpデモのページ(HTML・CSS・JavaScript がすべてこの中)

1. 画面を作る

時間を表示する場所と、3つのボタンを用意します。JavaScript から操作するので、それぞれに id をつけます。

index.php(11〜18行目) HTML
<div class="sw">
  <div id="timer" class="sw__time">00:00.000</div>
  <div class="sw__btns">
    <button id="start">スタート</button>
    <button id="stop">ストップ</button>
    <button id="reset">リセット</button>
  </div>
</div>

2. 必要な変数を準備する

ストップウォッチは「いつスタートしたか」「止めるまでに何秒たったか」「今動いているか」を覚えておく必要があります。それぞれを変数にします。

index.php(26〜38行目) JavaScript
(function() {
'use strict';

var timer = document.getElementById('timer');
var start = document.getElementById('start');
var stop  = document.getElementById('stop');
var reset = document.getElementById('reset');

var startTime;          // スタートを押した時刻
var elapsed = 0;        // 経過時間(ミリ秒)
var timerId;            // setTimeout の番号(止めるときに使う)
var timeToAdd = 0;      // 一度止めたときまでの経過時間
var isRunning = false;  // 動いているかどうか

3. 経過時間を「分:秒.ミリ秒」に直す

経過時間はミリ秒(1秒=1000ミリ秒)の数字で持っています。割り算と余り(%)で分・秒・ミリ秒に分けます。

index.php(40〜51行目) JavaScript
// 経過時間を「分:秒.ミリ秒」の形にして表示する
function updateTimerText() {
    var m  = Math.floor(elapsed / 60000);
    var s  = Math.floor(elapsed % 60000 / 1000);
    var ms = elapsed % 1000;

    m  = ('0' + m).slice(-2);    // 2桁にそろえる(5 → 05)
    s  = ('0' + s).slice(-2);
    ms = ('00' + ms).slice(-3);  // 3桁にそろえる(7 → 007)

    timer.textContent = m + ':' + s + '.' + ms;
}
式意味例:経過 83,456 ミリ秒
Math.floor(elapsed / 60000)60,000ミリ秒(1分)で割った整数部分 → 分1
Math.floor(elapsed % 60000 / 1000)1分に満たない余りを1000で割る → 秒23
elapsed % 10001秒に満たない余り → ミリ秒456

('0' + m).slice(-2) は「前に 0 を付けてから、後ろの2文字だけ取り出す」ことで、5 を 05 のように桁をそろえるテクニックです。ミリ秒は3桁なので ('00' + ms).slice(-3) にします(書き直す前はここが -2 になっていて、ミリ秒が正しく表示されていませんでした)。

4. 10ミリ秒ごとに表示を更新する

setTimeout(処理, 10) は「10ミリ秒後に処理を1回実行する」命令です。処理の最後で自分自身(countUp())をもう一度呼ぶことで、止めるまでずっとくり返します。

index.php(53〜60行目) JavaScript
// 10ミリ秒ごとに経過時間を計算し直す
function countUp() {
    timerId = setTimeout(function() {
        elapsed = Date.now() - startTime + timeToAdd;
        updateTimerText();
        countUp();
    }, 10);
}

経過時間は「足し算」ではなく、毎回 Date.now() - startTime で今の時刻との差を計算しています。setTimeout はぴったり10ミリ秒ごとに動く保証がないので、回数を数えるより正確になります。

5. スタート・ストップ・リセット

index.php(62〜87行目) JavaScript
start.addEventListener('click', function() {
    if (isRunning === true) {
        return;
    }
    isRunning = true;
    startTime = Date.now();
    countUp();
});

stop.addEventListener('click', function() {
    if (isRunning === false) {
        return;
    }
    isRunning = false;
    clearTimeout(timerId);
    timeToAdd += Date.now() - startTime;
});

reset.addEventListener('click', function() {
    if (isRunning === true) {
        return;
    }
    elapsed = 0;
    timeToAdd = 0;
    updateTimerText();
});
  • スタート:すでに動いていたら何もしません(二度押し対策)。今の時刻を startTime に覚えて、countUp() を始めます。
  • ストップ:clearTimeout(timerId) でくり返しを止めます。それまでの経過時間を timeToAdd にためておくので、もう一度スタートすると続きから計れます。
  • リセット:止まっているときだけ、経過時間を 0 に戻します。

「ラップ」ボタンを追加して、押したときの時間を下に一覧で表示するように改造してみましょう。timer.textContent の値をリストに追加するだけで作れます。

↑