初心者によるJavaScriptでストップウォッチを作るための学習 その1

# ストップウォッチの枠組みを作る

タイマー部分とStart、Stop、Resetボタンを用意し、ストップウォッチをつくります。

htmlの中身は

```html
  <div id="timer">00:00.000</div>
  <button id="start">Start</button>
  <button id="stop">Stop</button>
  <button id="reset">Reset</button>
```
と、こんな感じにタイマー部分とそれを操作するボタンを配置。

まずは個々のボタンが動くようにする。

```JavaScript:
{
  const timer = document.getElementById('timer');
  const start = document.getElementById('start');
  const stop = document.getElementById('stop');
  const reset = document.getElementById('reset');

  let startTime;
  let timeoutId;

  function countUp() {
    const d = new Date(Date.now() - startTime);
    const m = String(d.getMinutes()).padStart(2, '0');
    const s = String(d.getSeconds()).padStart(2, '0');
    const ms = String(d.getMilliseconds()).padStart(3, '0');
    timer.textContent = `${m}:${s}.${ms}`;

    timeoutId = setTimeout(() => {
      countUp();
    }, 10);
  }

  start.addEventListener('click', () => {
    startTime = Date.now();
    countUp();
  });

  stop.addEventListener('click', () => {
    clearTimeout(timeoutId);
  });

  reset.addEventListener('click', () => {
    timer.textContent = '00:00.000';
  });
}
```
ざっと大枠だけ用意するとこうなります。


## 問題点を解決する

ただしこれだと一旦ストップしてから再スタートすると、00:00:000から始まってしまう。これを止めた時点から再スタートさせたい。
つまりストップした時点の経過時間を保持し、それを足してから再スタートさせれば良い。

経過時間を`elapsedTime`と宣言し、0を入れておく（そうすることで最初のスタート時には影響されないようにする）

```JavaScript:
  let startTime;
  let timeoutId;
  let elapsedTime = 0; // タイマーが走っていた時間 === 経過時間
```


<br>
ストップした時点での経過時間は`Date.now() - startTime`で計算して`elapsedTime`に入れる。

```JavaScript:
  stop.addEventListener('click', () => {
    clearTimeout(timeoutId);
    elapsedTime = Date.now() - startTime; // ストップした時点での経過時間
  });
```

<br>
次にスタート時に経過時間を加える。

```JavaScript:
  const d = new Date(Date.now() - startTime + elapsedTime);
```

ただ、これでは、`elapsedTime`が直近の経過時間しか保持していないため、ストップ・スタートを繰り返すと時間が巻き戻ってしまうという問題がある。
従って


```JavaScript:
    elapsedTime += Date.now() - startTime; // ストップした時点での経過時間
```

のようにして、今までの経過時間を積み重ねるようにすれば良い。

これで良しと思いきや、リセットを押して再スタートした際は、前回ストップした状態から始まってしまうので、

```JavaScript:
  reset.addEventListener('click', () => {
    timer.textContent = '00:00.000';
    elapsedTime = 0; // 経過時間のリセット
  });
```
として、経過時間もリセットすることを忘れないようにする。

## 通常の操作以外で発生する問題点を解決する

ストップウォッチとしては動くのですが、いくつかの問題点を抱えたままです。

**1.スタートを複数回押した後だと、一回ストップを押しても止まらない
2.ストップを複数回押した後だと、正しい秒数から再開されない**

これらは主に、各ボタンを押すごとにstartTimeやelapsedTimeが多重起動してしまうことが原因です。

それらの解決方法は、**必要のないボタンは押させない**が良いでしょう。フォールトアボイダンスというやつですね。

### ボタンの状態を管理する

必要のないボタンを押させない状態は.disabled = true;と設定します。

従って、各状態ごとの状況は

|           |Start      |Stop       |Reset      |
|:----------|:----------|:----------|:----------|
|開始時(Initual)     |false |true |true  |
|カウント中(Running) |true |false |true  |
|停止時(Stopped)     |false |true |false  |
※true = 非表示 / false = 表示

となります。

これを設定するのは

```JavaScript:
  function setButtonStateInitial() {
    start.disabled = false;
    stop.disabled = true;
    reset.disabled = true;
  }
  function setButtonStateRunning() {
    start.disabled = true;
    stop.disabled = false;
    reset.disabled = true;

  }
  function setButtonStateStopped() {
    start.disabled = false;
    stop.disabled = true;
    reset.disabled = false;

  }

  setButtonStateInitial(); //初期設定

```
とし、各ボタンをクリックしたときの関数に、これらの状態へ移行するようにする。


```JavaScript:
  start.addEventListener('click', () => {
    setButtonStateRunning(); // Runningへ以降
    startTime = Date.now();
    countUp();
  });
  stop.addEventListener('click', () => {
    setButtonStateStopped(); // Stoppedへ以降
    clearTimeout(timeoutId);
    elapsedTime += Date.now() - startTime;
   console.log(elapsedTime);
  });
  reset.addEventListener('click', () => {
    setButtonStateInitial(); // Initualへ以降
    timer.textContent = '00:00.000';
    elapsedTime = 0;
  });
```

これで操作そのものの問題は概ね解決できたと思います。

続きは装飾部分について取り組みます。その2はこちら　https://qiita.com/amanomunt/items/b667aaea307f1df6b104