JavaScriptをつかってストップウォッチを作成する　後半

## どんどん増えるログを出す
### スタートボタンが押されたら「開始」と表示する
一通りストップウォッチ本体のコードは書けた。最後の仕様であった、ボタンを押した際のログを作成する。
setIntervalを呼び出した後に新しくHTMLタグを追加するコードを書く。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>StopWatch</title>
  <link href = "./main.css" rel = "stylesheet">
</head>
<body>
  <h1 class = "title">ストップウォッチ</h1>
  <div id = "stopWatchPanel">
   <div class = "display">0</div>
   <div class = "actions">
     <button class = "startButton">スタート</button>
     <button class = "stopButton">ストップ</button>
   </div>
   <div class = "log"></div>
  </div>
  <script src = "./main.js"></script>
</body>
</html>
```

```javascript:main.js
//startButtonというclassがついているタグ要素のうち、
//最初のもの（スタートボタン）を取り出す
let displayElm = document.getElementsByClassName("display")[0];
let timer = null;

let startButton = document.getElementsByClassName("startButton")[0];
//取り出したstartButtonに対してクリックイベントのリスナを仕掛ける
startButton.addEventListener("click",function(){
  //この行はクリックしたときよばれる
  if(timer === null){
  let seconds = 0;
  timer = setInterval(function(){
    seconds++;
    displayElm.innerText = seconds;
    console.log(seconds);
  },1000); 

  let message = "開始";①
  let messageElm = document.createElement("div");①
  messageElm.innerText = message;①
  let logElm = document.querySelector(".log");①
  logElm.appendChild(messageElm);①
 }
  console.log("start" + timer);
});

let stopButton = document.getElementsByClassName("stopButton")[0];
stopButton.addEventListener("click",function(){
  if(timer !== null){
  console.log("stop" + timer);
clearInterval(timer);
timer = null;
  }
});
```

追加したコードを順にみていく。①
document.createElementは、指定した名前のHTMLタグ要素を作成することができる関数である。
今回はdivを指定しているので、divタグが作成される。これをmessageElmという変数に代入した。
さらにinnerTextを使って、"開始"という文字列をタグ内に表示した。

:::note info
message = "開始"
messageElmという変数にdivタグを生成する役割をもたせた
messageElm.innerText = "message"
で、divタグ内にmessageという変数をかいた。今、message = "開始"なので
結論、
<div>"開始"</div>
をつくった、ということ。
:::
次のdocument.querySelectorは、引数に与えたCSSセレクタに合致する画面上のHTMLタグを一つ取得する。ここでは、class = "log"と指定されたタグを指し示す変数としてlogElmが初期化される。
document.getElementsByClassNameより汎用的な機能があるため、こちらを利用するケースも多い。
最後にlogElm.appendChildを使って、作成したタグをlogElmの子として追加している。この追加が行われると、messageElmが指している要素がブラウザに描画される。

:::note info
logElm = logクラスをもつタグを示す変数
logElm.appendChildを使って、作成したタグ（messageElm）をlogElmの子として追加
messageElmのinnerTextには"開始"が入るので、こんなイメージ 
```html:index.html
<div class = "log">
 <div>開始</div>
</div>
```
:::
![671d65ec20437bf6e38ac760c97c39bd.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884270/3213d6ab-696a-6662-826a-f47652203cb6.gif)
これでスタートボタンを押すたびに、ストップウォッチ本体の下に「開始」と追加される。

### ストップボタンが押されたら「終了」と表示する
こちらでは、スタートボタンを追加した時のコードを使いまわせるほど似ている。（開始が終了になるだけ）
そこで、一度リファクタリングを行なってから、コードを整理して実装する。

<details><summary>リファクタリングとは</summary>

コードの動作を変えずに、処理を整理すること。リファクタリングをして今後の修正への見通しを良くしてから、改めて修正を進めると誤りが減る

</details>
リファクタリングして、新たにaddMessageという関数を作成した。①
中身はほとんど先の内容を移動し、引数の名前などを整えている。また、logElmの取得はスタートボタンのイベントハンドラの外にだして、今後ストップボタンの処理でも利用できるようにした。
logElmが引数で渡されていないのにaddMessage内で利用されていることが不思議に感じるかもしれない。②
JavaScriptでは関数と並列以上にある変数を束縛できる性質がある。ここでは、「宣言が関数の外にあっても利用できる場合がある」と考えてよい。

```javascript:main.js
function addMessage(message){①
  let messageElm = document.createElement("div");
  messageElm.innerText = message;
  logElm.appendChild(messageElm);②
}
let displayElm = document.getElementsByClassName("display")[0];
let logElm = document.querySelector(".log");
let timer = null;
let startButton = document.getElementsByClassName("startButton")[0];

startButton.addEventListener("click",function(){
  if(timer === null){
  let seconds = 0;
  timer = setInterval(function(){
    seconds++;
    displayElm.innerText = seconds;
    console.log(seconds);
  },1000); 

  addMessage("開始");
 }
  console.log("start" + timer);
});

let stopButton = document.getElementsByClassName("stopButton")[0];
stopButton.addEventListener("click",function(){
  if(timer !== null){
  console.log("stop" + timer);
clearInterval(timer);
timer = null;
  }
});
```
リファクタリングでは動作は変わらないので、コードが変わっても同じように動作する。
ここまでコードを書ければ、ストップボタン側に一行追加するだけで「終了」を表示できる。適切に処理をまとめていくことで楽に作成することができる。

```javascript:main.js
function addMessage(message){
  let messageElm = document.createElement("div");
  messageElm.innerText = message;
  logElm.appendChild(messageElm);
}
let displayElm = document.getElementsByClassName("display")[0];
let logElm = document.querySelector(".log");
let timer = null;
let startButton = document.getElementsByClassName("startButton")[0];

startButton.addEventListener("click",function(){
  if(timer === null){
  let seconds = 0;
  timer = setInterval(function(){
    seconds++;
    displayElm.innerText = seconds;
    console.log(seconds);
  },1000); 

  addMessage("開始");
 }
  console.log("start" + timer);
});

let stopButton = document.getElementsByClassName("stopButton")[0];
stopButton.addEventListener("click",function(){
  if(timer !== null){
  console.log("stop" + timer);
clearInterval(timer);
timer = null;
addMessage("終了");
  }
});
```

### ログに時刻を入れ、見た目を整える
スタート、ストップのログの仕様としてはメッセージとともに時刻を表示する動きになるので、時刻表示を作っていく。 addMessageの中身だけを修正すればスタートのときもストップの時も一緒に反映される。

```javascript:main.js
function addMessage(message){
  let messageElm = document.createElement("div");
  let now = new Date();①
  messageElm.innerText = now.getHours() + "時" + now.getMinutes() + "分" + now.getSeconds() + "秒" + message;②
  messageElm.classList = ["message"];③
  logElm.appendChild(messageElm);
}
let displayElm = document.getElementsByClassName("display")[0];
let logElm = document.querySelector(".log");
let timer = null;
let startButton = document.getElementsByClassName("startButton")[0];

startButton.addEventListener("click",function(){
  if(timer === null){
  let seconds = 0;
  timer = setInterval(function(){
    seconds++;
    displayElm.innerText = seconds;
    console.log(seconds);
  },1000); 

  addMessage("開始");
 }
  console.log("start" + timer);
});

let stopButton = document.getElementsByClassName("stopButton")[0];
stopButton.addEventListener("click",function(){
  if(timer !== null){
  console.log("stop" + timer);
clearInterval(timer);
timer = null;
addMessage("終了");
  }
});
```
new Date()の戻り値は現在時刻の情報を格納したオブジェクトである。今回はnowという変数でそれを利用できるようにした。①
now.getHours()、now.getMinutes()、now.getSeconds()などの呼び出しを行うと現在時刻の情報の塊から、時・分・秒のそれぞれの要素を取り出すことができるようになっている。これをmessageの前に連結して出している。②
また、messageElmに**クラスとして**"message"を追加した。③これにより、新規作成されるdivタグには、class = "message"がつく。先にあったcssの内容が反映されて、ログにはアンダーラインが入る。これで動作としては仕様通りになった。
![cad37d15e30ba8f3daedb9ef272823c9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884270/eb52905d-faad-0ce3-6358-811cfa94a95e.gif)

## リファクタリングして柔軟性を上げる
### ストップウォッチを一つの関数にしてみる
ここまでのアプリケーション作成で、仕様通りのものをつくることができた。このまま完成でもよいが、もう一段階良いものにするためにリファクタリングを行う。
ストップウォッチの機能を自分の思ったタイミングで導入できるようにしてみる。
そのためにstopWatchという関数を作る。中身はこれまでに書いたコードを全て入れる。

```javascript:main.js
 function stopWatch(){ 
  function addMessage(message){
    let messageElm = document.createElement("div");
    let now = new Date();
    messageElm.innerText = now.getHours() + "時" + now.getMinutes() + "分" + now.getSeconds() + "秒" + message;
    messageElm.classList = ["message"];
    logElm.appendChild(messageElm);
  }
  let displayElm = document.getElementsByClassName("display")[0];
  let logElm = document.querySelector(".log");
  let timer = null;
  let startButton = document.getElementsByClassName("startButton")[0];

  startButton.addEventListener("click",function(){
    if(timer === null){
    let seconds = 0;
    timer = setInterval(function(){
      seconds++;
      displayElm.innerText = seconds;
      console.log(seconds);
    },1000); 

    addMessage("開始");
  }
    console.log("start" + timer);
  });

  let stopButton = document.getElementsByClassName("stopButton")[0];
  stopButton.addEventListener("click",function(){
    if(timer !== null){
    console.log("stop" + timer);
  clearInterval(timer);
  timer = null;
  addMessage("終了");
    }
  });
}

stopWatch();
```
これで、想定している構造をもっているHTMLに対してであれば簡単にストップウォッチを導入できるようになった。



> 参考文献
[ステップアップJavaScript フロントエンド開発の初級から中級へ進むために](https://www.amazon.co.jp/%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%82%A2%E3%83%83%E3%83%97JavaScript-%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E9%96%8B%E7%99%BA%E3%81%AE%E5%88%9D%E7%B4%9A%E3%81%8B%E3%82%89%E4%B8%AD%E7%B4%9A%E3%81%B8%E9%80%B2%E3%82%80%E3%81%9F%E3%82%81%E3%81%AB-%E4%BD%90%E8%97%A4-%E6%AD%A3%E5%BF%97-ebook/dp/B09MQ53Y2C/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=9G912ZQLVXY8&keywords=%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%82%A2%E3%83%83%E3%83%97JavaScript&qid=1669644043&qu=eyJxc2MiOiIxLjEwIiwicXNhIjoiMS4wMCIsInFzcCI6IjAuOTcifQ%3D%3D&sprefix=%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%82%A2%E3%83%83%E3%83%97javascript%2Caps%2C293&sr=8-1)