JavaScript | 1 日 90 分 × 7 日アプリ学習:タイマーアプリ(初級編)

JavaScript
スポンサーリンク

7日目のゴールと今日やること

引用元は。なし(本教材はオリジナル解説です)

7日目のテーマは
「この1週間で作ってきたタイマーを、“自分のアプリ”として完成させる」
ことです。

新しい文法はほとんど増やしません。
やるのは、これまで学んだものの「整理」と「理解の言語化」です。

setTimeout
状態管理(isRunning・elapsed・timerId)
開始・停止・リセットの役割
画面表示(00:00)と UI 制御

これらを、ひとつの完成したタイマーアプリとしてまとめていきます。


タイマーアプリの「全体像」を言葉で説明してみる

タイマーが動く仕組みをストーリーで捉える

あなたが作ってきたタイマーは、
ざっくり言うとこんなストーリーで動いています。

開始ボタンを押す
→ isRunning を true にする
→ setTimeout で 1秒後の tick を予約する
→ tick が呼ばれるたびに elapsed を増やす
→ updateDisplay で画面の数字を更新する
→ tick の最後でまた次の tick を予約する
→ 停止で予約をキャンセルする
→ リセットで時間を 0 に戻し、画面も 00:00 にする

この流れを、
「コードとして」だけでなく
「物語として」説明できるようになると、
あなたの理解はかなり深いところまで来ています。


コアとなる3つの状態管理を整理する

isRunning:タイマーが動いているかどうか

isRunning は、
タイマーの「今の状態」を表すフラグです。

true → 動いている
false → 止まっている

この1ビットの情報が、
開始・停止・リセット・UI制御のすべての前提になっています。

開始時には true にする
停止・リセット時には false にする
tick の中では「本当に動いているか?」を確認する

というふうに、
アプリ全体の“前提条件”を支えているのが isRunning です。

elapsed:経過時間(ミリ秒)

elapsed は、
「タイマーがどれだけ進んだか」を記録する変数です。

tick が呼ばれるたびに 1000 足される
updateDisplay で「分:秒」に変換される
リセットで 0 に戻される

時間もただの数値として扱える、
という感覚をここでしっかり掴んでおいてください。

timerId:次の tick の予約番号

timerId は、
setTimeout が返してくる「予約の番号」です。

startTimer で最初の tick を予約するときに代入される
tick の中で「次の tick」を予約するときに更新される
stopTimer や resetTimer で clearTimeout に渡される

「止めたいなら、どの予約を止めるかを覚えておく必要がある」
その“手がかり”が timerId です。


setTimeout と tick の関係をもう一度深掘りする

setTimeout は「1回だけ」、繰り返しは自分で作る

setTimeout 自体は「1回だけ実行する予約」です。

だからこそ、
あなたは tick の中でこう書きました。

timerId = setTimeout(tick, 1000);
JavaScript

これによって、

tick が呼ばれる
→ elapsed を増やす
→ 画面を更新する
→ 「1秒後の tick」を予約する

というループが生まれます。

ここで重要なのは、
「JavaScript が勝手に繰り返しているのではなく、自分で繰り返しを作っている」
という感覚です。

tick は「心臓」、setTimeout は「鼓動の間隔」

tick は、
タイマーの「1回ぶんの鼓動」です。

setTimeout は、
「次の鼓動までの間隔」を決める役割です。

この2つを分けて考えられるようになると、
時間を扱うアプリ全般に応用が効くようになります。


開始・停止・リセットを“設計の目線”で見直す

開始 startTimer の責務

startTimer の仕事は、
「タイマーを動き始めさせること」です。

すでに動いているなら何もしない
動いていなければ isRunning を true にする
最初の tick を予約する
UI(ボタンや色)を「動作中」の状態にする

ここで大事なのは、
「開始は“初動”だけを担当し、その後の繰り返しは tick に任せる」
という役割分担です。

停止 stopTimer の責務

stopTimer の仕事は、
「今後の tick を止めること」です。

動いていなければ何もしない
clearTimeout(timerId) で次の tick をキャンセルする
isRunning を false にする
UI を「停止中」の状態に戻す

elapsed はそのまま残すことで、
再開したときに続きから動かせるようになっています。

リセット resetTimer の責務

resetTimer の仕事は、
「タイマーを“初期状態”に戻すこと」です。

動いていればまず止める(clearTimeout)
isRunning を false にする
timerId を null にする
elapsed を 0 に戻す
画面表示を 00:00 に更新する
UI を「初期状態」にする

「停止」と「リセット」は違う
というのを、ここでしっかり区別できているなら、
あなたの設計力はかなりいい線いっています。


7日目の完成コード(シンプル版)

全体を通して読める形にまとめる

<div id="timer-container">
  <div id="display">00:00</div>

  <div id="buttons">
    <button id="startBtn" onclick="startTimer()">開始</button>
    <button id="stopBtn" onclick="stopTimer()">停止</button>
    <button id="resetBtn" onclick="resetTimer()">リセット</button>
  </div>
</div>
#timer-container {
  width: 200px;
  margin: 40px auto;
  text-align: center;
  font-family: sans-serif;
}

#display {
  font-size: 48px;
  font-weight: bold;
  padding: 20px;
  border: 3px solid #333;
  border-radius: 10px;
  margin-bottom: 20px;
  background-color: #f8f8f8;
}

#buttons button {
  font-size: 16px;
  padding: 10px 20px;
  margin: 5px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
}

button:disabled {
  background-color: #ccc;
  color: #666;
  cursor: not-allowed;
}
let timerId = null;
let isRunning = false;
let elapsed = 0;

function pad(num) {
  return num.toString().padStart(2, "0");
}

function updateDisplay() {
  const totalSeconds = Math.floor(elapsed / 1000);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  document.getElementById("display").innerText =
    pad(minutes) + ":" + pad(seconds);
}

function updateButtons() {
  document.getElementById("startBtn").disabled = isRunning;
  document.getElementById("stopBtn").disabled = !isRunning;
  document.getElementById("resetBtn").disabled = false;
}

function updateDisplayStyle() {
  const display = document.getElementById("display");
  display.style.backgroundColor = isRunning ? "#e0ffe0" : "#f8f8f8";
}

function tick() {
  if (!isRunning) return;

  elapsed += 1000;
  updateDisplay();

  timerId = setTimeout(tick, 1000);
}

function startTimer() {
  if (isRunning) return;

  isRunning = true;
  updateButtons();
  updateDisplayStyle();

  timerId = setTimeout(tick, 1000);
}

function stopTimer() {
  if (!isRunning) return;

  clearTimeout(timerId);
  timerId = null;
  isRunning = false;

  updateButtons();
  updateDisplayStyle();
}

function resetTimer() {
  if (isRunning) {
    clearTimeout(timerId);
  }

  isRunning = false;
  timerId = null;
  elapsed = 0;

  updateDisplay();
  updateButtons();
  updateDisplayStyle();
}
JavaScript

このコードを、
「ただコピペして動かす」のではなく、
「一行ずつ、自分の言葉で説明できるか」 を意識して読んでみてください。


7日目で特に深く理解してほしいこと

setTimeout と状態管理は「セットで考える」

setTimeout で時間を扱うときは、
必ず「状態管理」とセットになります。

今動いているのか
次に何が起こる予定なのか
止めるときに何をキャンセルすべきか

これを全部、
isRunning・elapsed・timerId で表現している、
という構造を意識してください。

機能は「責務」で分けると整理しやすい

開始
停止
リセット

この3つは、
似ているようでやっていることが違います。

「この関数の責務は何か?」
と自分に問いながらコードを書くと、
自然と読みやすく、壊れにくい設計になります。


この7日間のタイマー学習を、自分のものにするために

ここまでであなたは、

setTimeout で時間を扱える
状態管理でアプリの流れをコントロールできる
開始・停止・リセットを設計として分けて考えられる
HTML・CSS・JavaScript を組み合わせて、
ひとつの「タイマーアプリ」を作れる

というところまで来ています。

最後に、ひとつだけ問いを投げます。

このタイマーを、
「あなたの生活のどこで使えそうか」
少しだけ想像してみてください。

勉強時間の計測
作業のポモドーロタイマー
筋トレのインターバル

その「使い道」が見えた瞬間、
この7日間で書いたコードは、
ただの練習ではなく、
あなたのための“道具”になります。

タイトルとURLをコピーしました