Javascriptで電卓を作ろう　第3回　連続で演算記号や小数点が押された場合の処理

# 目次
[第1回 HTMLとCSSでボタンを横並びで配置しよう](https://qiita.com/rikimaru914/items/3412df78570b48b25858)

- HTMLのマークアップ
- CSS

[第2回　四則演算をやってみよう](https://qiita.com/rikimaru914/items/a30d83c43a4eb14ba999)

- Javascriptで四則演算
- 6)打った数字をコンソールに出力させよう。
- 7)打った数字と記号を変数に格納しよう。
- 8)計算結果を出力させよう。
- 8-1）「＝」を押した時の処理
- 8-2) 計算結果と計算途中を、計算結果の画面に表示させよう。

[第3回　連続で演算記号や小数点が押された場合の不具合処理（今回の記事はここ）](https://qiita.com/rikimaru914/items/8df2124165afd036b922)

- 最初に「＋　÷　－　×　＝」が押せないようにする。
- 二回連続で計算できるようにしたい。
- Cボタン（リセットボタン）を押した時の処理
- BS(バックスペース）ボタンを使えるようにする

[第4回　小数点を正しく入力できるように改善しよう](https://qiita.com/rikimaru914/items/61154567fa02f1d7a156)

- 12.12.や12..12のように小数点を間違って入力できてしまう。
- 電卓と同じように「.4」と入力したら0.4としたい。

[第5回　演算記号を連続して押した時の不具合を直そう](https://qiita.com/rikimaru914/items/21b3e4532f0de41db9a6)

[第6回　バックスペースを押した時の不具合を直そう。](https://qiita.com/rikimaru914/items/0a97bfa685620f16cd5b)

[第7回　「4 ÷ 3 = 1.33333333333333333」の表示桁数を10桁にしたい](https://qiita.com/rikimaru914/items/c3f23aa65f4be780d6ff)

[第8回　「電卓と同じ表示画面を目指そう](https://qiita.com/rikimaru914/items/b4076b2673014034076c)

前回作成した電卓について、以下のような点を改善したいと思います。
[前回の記事](https://qiita.com/rikimaru914/items/a30d83c43a4eb14ba999)

- 最初に「＋　÷　－　＝　.」が押せてしまう。
- 最初に0を連続で押せてしまう（00.4や00043となってしまう）
- 記号ボタン（＋×÷-)を連続で押せてしまい、計算ができない。（連続で押した場合は、最後に押した記号ボタンが採用されるようにしたい。
）
- 一度計算をやると、次の計算をするときに、ブラウザを読み込みなおさないといけない（Cボタンを使えるようにしよう）
- 前回の計算結果を利用して、次の計算を行いたい。
- 12.12.や12..12のように間違った小数を入力できてしまう。
- 電卓と同じように「.4」と入力したら0.4としたい。
- 一度間違えると、全てやり直し（BSボタンを使えるようにしたい）
- 割り算等で小数点以下が異様に長い。

#対処法1)
これらに対応するため、状態を定義します。
1)計算する前の最初の状態（start）　
2)数字を入力している最中（calculation）
3)「＋　÷　－　×　＝」を押した直後（calBtn）
4)「＝」を教えて計算が終わった直後（finish）
stateという変数を用意して、star,calculation, calBtn, finishを代入して状態を管理します。

**1)計算する前の最初の状態（start）**　

- 「＋　÷　－　×　＝」が押せないようにする。
- 0を連続で押された時に、1つだけ入力するようにしたい。


**2)数字を入力している最中（calculation）**

- どのボタンも押せる

**3)「＋　÷　－　×」を押した直後（calBtn）**

- 「＋　÷　－　×」を連続で押した場合、最後に押されたボタンを採用するようにしたい。
- 0を連続で押された時に、1つだけ入力するようにしたい。

**4)「＝」を教えて計算が終わった直後（finish）**

- 「＝」を押せないようにしたい。
- 次に数字が押された場合、前の計算結果はリセットして、計算をやり直す。
- 次に「＋　÷　－　×」を押した場合、前回の計算結果を利用して続けて計算する。

#対処法2)
これとは別に整数入力中、小数入力中を別の変数で定義します。
modeという変数を用意して、integer_mode、decimal_modeを代入して状態を管理します。
a)整数入力中（integer_mode)

-「.」小数点を押すことができる。

b)小数入力中（decimal_mode)

-　「＋　÷　－　×」が押されるまでは、「.」小数点を押すことができない。（12.12.4や12..12とならないようにしたい）

#HTMLの変更
現在は、0,1,2,3,4,5,6,7,8,9,+,-,*,/全てが同じclass＝"num_bth"となっているが、状態管理するにあたり、「0」「1-9」「+,-,*,/」｢.(point)｣を別の変数で管理することにします。
「0」　id="zero"
「1-9  class="one_nine"
「+,-,*,/」class="cal"
｢.　(point)｣ id="point"

HTML変更後

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>電卓</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div class = "container">
<!--      計算結果を出力する場所-->
      <div id = "output_total" class="output ">0</div>
<!--      計算過程を出力する -->
      <div id = "output_sub" class="output active">0</div>
<!--      ボタンを配置する場所-->
      <div class = 'input'>
          <section class = 'row'>
            <div id = "clear">C</div>
            <div class = "num_bth"></div>
            <div class = "num_bth" id="bs">BS</div>       
            <div class = "num_bth cal" data-index-id= '/'>÷</div>
          </section>
          <section class = 'row'>
            <div class = "num_bth one_nine " data-index-id = 9 >9</div>
            <div class = "num_bth one_nine " data-index-id = 8 >8</div>
            <div class = "num_bth one_nine " data-index-id = 7 >7</div>
            <div class = "num_bth cal" data-index-id = '*'>×</div>
          </section>
          <section class = 'row'>
            <div class = "num_bth one_nine " data-index-id = 6 >6</div>
            <div class = "num_bth one_nine " data-index-id = 5 >5</div>
            <div class = "num_bth one_nine " data-index-id = 4 >4</div>
            <div class = "num_bth cal" data-index-id = '-'>－</div>     
          </section>
          <section class = 'row'>
            <div class = "num_bth one_nine " data-index-id = 3 >3</div>
            <div class = "num_bth  one_nine" data-index-id = 2 >2</div>
            <div class = "num_bth  one_nine" data-index-id = 1 >1</div>
            <div class = "num_bth cal" data-index-id = '+'>+</div>
          </section>
          <section class = 'row'>
            <div class = "num_bth" data-index-id = 00 >00</div>
            <div class = "num_bth" id="zero" data-index-id = 0 >0</div>
            <div class = "num_bth" id="point" data-index-id = . >.</div>
            <div id = 'equal_btn'>=</div>       
          </section>        
      </div>
    </div>
    <script src="script.js"></script>
  </body>
</html>
```

#JavascriptをHTMLに合わせて、「0」「1-9」「+,-,*,/」｢.(point)｣が押せるように以下のように変更

```javascript
'use strict'
{
  const num_bth = document.querySelectorAll('.num_bth');
  let output_sub = document.getElementById('output_sub');//計算結果を表示する場所
  const output_total = document.getElementById('output_total');//計算過程を表示する場所
  let total = 0;//計算式を表す変数 
  let state = 'start';//最初の状態を定義
  let mode = 'integer_mode'; //最初は整数入力モード
  
  // 1-9の数字ボタンを押した時
    const one_nine = document.querySelectorAll('.one_nine');
    one_nine.forEach(index => {     
      index.addEventListener('click', () => {
        console.log(index.dataset.indexId)
        if(total === 0) {
          total = index.dataset.indexId;  
        }else{
          total += index.dataset.indexId;
        }      
        output_sub.textContent = total;
        state = 'calculation'//数字を入力している状態にする。
      }) //click   
    })//forEach
  
  // 0の数字ボタンを押した時
  const zero = document.getElementById('zero');
  zero.addEventListener('click', () => {
    console.log(zero.dataset.indexId)
    if(total === 0) {
      total = zero.dataset.indexId;  
    }else{
      total += zero.dataset.indexId;
    }      
    output_sub.textContent = total;
    state = 'calculation'//数字を入力している状態にする。
  }) //click    
  
  // 「.」小数点ボタンを押した時
  const point = document.getElementById('point');
  point.addEventListener('click', () => {
    console.log(point.dataset.indexId)
    if(total === 0) {
      total = point.dataset.indexId;  
    }else{
      total += point.dataset.indexId;
    }      
    output_sub.textContent = total;
    state = 'calculation'//数字を入力している状態にする。
    mode = 'decimal_mode'; //小数入力モードに変更
  }) //click  
  
  //「＋　÷　－　×」ボタンを押した時
  const cal = document.querySelectorAll('.cal');
  cal.forEach(index => {     
    index.addEventListener('click', () => {
      console.log(index.dataset.indexId)
      if(total === 0) {
        total = index.dataset.indexId;  
      }else{
        total += index.dataset.indexId;
      }      
      output_sub.textContent = total;
      state = 'calBtn'//演算記号を入力している状態する。
    }) //click   
  })//forEach
  
  //イコールを押した時
  const equal_btn = document.getElementById('equal_btn');
  equal_btn.addEventListener('click',() =>{
    console.log(eval(total));
    output_total.textContent = eval(total);
  });
  
}
```

#最初に「＋　÷　－　×　＝」が押せないようにする。
最初の状態の時state==='start'に演算記号が押せないようにしたい。

```javascript
  if(state === 'start') {
        return;
      }
```
returnとし、以下の処理が行われないようにすればよい。

```javascript
  //「＋　÷　－　×」ボタンを押した時
  const cal = document.querySelectorAll('.cal');
  cal.forEach(index => {     
    index.addEventListener('click', () => {
      if(state === 'start') {
        return;
      }
      console.log(index.dataset.indexId)
      if(total === 0) {
        total = index.dataset.indexId;  
      }else{
        total += index.dataset.indexId;
      }      
      output_sub.textContent = total;
      state = 'calBtn'//演算記号を入力している状態する。
    }) //click   
  })//forEach
```

#二回連続で計算できるようにしたい。
「2 × 3 ＝ 6」「6 + 2 = 8」を連続して計算しようとするとこうなってしまう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320736/9487b13a-7bda-0d9a-776c-3e1695cf61a2.png)
電卓の機能として以下のようにしたい。

- 計算後に数字を押した場合：前回の結果はリセットして計算しなおす。
- 計算後に演算記号を押した場合：前回の計算結果にを再度利用して計算を続ける。

というわけで、数字を押した時、記号を押した時の各stateごとの処理は以下の通り。
**数字を押した時**

- 最初（state==="start"):
    - totalに打った数字を代入する
    - stateをcalculationに変える
- 計算後（state==="finish")
    - リセット処理後に、totalに打った数字を代入する
    - stateを計算中（calculation）に変える
- 計算中（state==="calculation")
    - totalに打った数字を追加して、totalに代入する。
    
```javascript
  // 1-9の数字ボタンを押した時
    const one_nine = document.querySelectorAll('.one_nine');
    one_nine.forEach(index => {     
      index.addEventListener('click', () => {
        if(state === 'start') {
          //最初totalに打った数字を代入する
          total = index.dataset.indexId;         
        }else if(state === 'finish') {
          //計算後、リセット処理後に、totalに打った数字を代入する
          reset();
          total = index.dataset.indexId;  
        }else if(state === 'calculation'){
          //計算中totalに打った数字を追加して、totalに代入する。
          total += index.dataset.indexId;
        }     
        output_sub.textContent = total;
        state = 'calculation'//数字を入力している状態にする。
      }) //click   
    })//forEach


//リセットを行う関数
  function reset() {
   total = 0; 
   output_sub.textContent = 0;
   output_total.textContent = 0;
  }
```



**記号を押した時**

- 最初（state="start"):
    - 記号は押せないようにする
- 計算後（state="finish")
    - 前の計算結果をtotal に代入して計算しなおす。
    - 計算過程表示画面に前の計算結果を表示させる。
    - 計算結果表示画面を"0"を表示させる。
    - stateを演算記号を押した後（calBtn）に変える
- 計算中（state="calculation")
    - totalに打った記号を追加し、totalに代入する。
    - - stateを演算記号を押した後（calBtn）に変える

```javascript
  //「＋　÷　－　×」ボタンを押した時
  const cal = document.querySelectorAll('.cal');
  cal.forEach(index => {     
    index.addEventListener('click', () => {
      if(state === 'start') {
        return;//最初記号は押せない
      }else if(state === 'calculation'){
        total += index.dataset.indexId;//計算中はtotalに打った記号を追加し、totalに代入する。
      }else if(state === 'finish'){
        //計算後は前の計算結果をtotal に代入して計算しなおす。
        total = output_total.textContent;
        total += index.dataset.indexId;
        output_total.textContent = 0
      }
      console.log(index.dataset.indexId)   
      output_sub.textContent = total;
      state = 'calBtn'//演算記号を入力している状態する。
    }) //click   
  })//forEach
```

#Cボタン（リセットボタン）を押した時の処理
```javascript
  //Cボタン（リセットボタン）を押した時の処理
  const clear = document.getElementById('clear')
  clear.addEventListener('click', () => {
    reset();
  })
  
 //リセットを行う関数
  function reset() {
   total = 0; 
   output_sub.textContent = 0;
   output_total.textContent = 0;
  }
```

#BS(バックスペース）ボタンを使えるようにする

- 計算終了後(state === "finish") はBSを押せない。
- それ以外の時は、最初から最後から二文字目までをtotalに代入（最後の一文字を除きtotalに代入する）

slice関数
str.slice(0, -1) は、文字列から 0 番目の文字から最後から 2 番目の文字までを取り出します。
[String​.prototype​.slice()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/slice)

```javascript
  //BSボタン（バックスペース）を押した時の処理
  const bs = document.getElementById('bs')
  bs.addEventListener('click', () => {
    console.log('BS')
    if(state ==="finish") {
      return;//計算後は、bsを押せない。
    }else{
//      一文字目から、最後から二文字目までをtotalに代入（最後の一文字を除きtotalに代入する）
      total = output_sub.textContent.slice(0, -1);
      output_sub.textContent = total;
    }
  })
```

#残りの課題　次回へ 
~~- 最初に「＋　÷　－　＝　.」が押せてしまう。~~
- 最初に0を連続で押せてしまう（00.4や00043となってしまう）
- 記号ボタン（＋×÷-)を連続で押せてしまい、計算ができない。（連続で押した場合は、最後に押した記号ボタンが採用されるようにしたい。
~~- 一度計算をやると、次の計算をするときに、ブラウザを読み込みなおさないといけない（Cボタンを使えるようにしよう）~~
~~- 前回の計算結果を利用して、次の計算を行いたい。~~
- 12.12.や12..12のように間違った小数を入力できてしまう。
- 電卓と同じように「.4」と入力したら0.4としたい。
~~- 一度間違えると、全てやり直し（BSボタンを使えるようにしたい）~~
- 割り算等で小数点以下が異様に長い。