HTML、CSS、JavaScriptで電卓をつくりました。

## 作ったアプリ
https://xenodochial-clarke-ebb79c.netlify.app/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681683/0bada3f2-a29c-839a-04bd-4c97967bc4f2.png)
四則演算だけできるシンプルな電卓です。できるだけWindows10の標準電卓と同じ動作になるように作りました。
今回は外部ライブラリとして、fittyとbig.jsを使いました。

https://github.com/rikschennink/fitty

https://github.com/MikeMcl/big.js/

## 今回学んだこと
### 数字が枠からはみ出る問題
電卓のディスプレイ部分ですが、入力する数字の桁が大きすぎると枠からはみ出てしまいます。
[1]を連打しまくる...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681683/2c2d2285-0f47-a7fc-344d-eaf623320955.png)
で、数字が何桁入力されても幅が固定された枠の中に収まるように文字サイズを調整したい！と思っていたところ、fittyというとっても便利なライブラリを見つけました。fittyを使うと、次のようにフォントサイズが自動調整されて桁数が多い場合でも、1行に収まるようにしてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681683/755a682d-8b37-b450-dd44-3de08e040cfa.png)

```html:index.html
<div class="result-display"><div><div id="result">0</div></div></div>
```

```javascript:script.js
import fitty from './fitty.module.js'
fitty('#result', {
  maxSize: 48 // 最大フォントサイズを設定。最小も設定できる。
})
```

使い方はとても簡単で、`fitty`に文字サイズを自動調整したい要素を渡して、最大／最小フォントサイズなどを設定するだけです。一見必要なさそうな`<div>`は、`.result-display`に`padding`を設定しているためで、公式の次の注意に従っています。

>If the parent element of the fitty element has horizontal padding the width calculation will be incorrect. You can fix this by wrapping the fitty element in another element.

電卓としては１行に収めてくれるとはいえ、あまり桁数が多くなっても困るので数字を入力するたびにその桁数を評価して16桁以上は入力できないようにしています。

```JavaScript:script.js
if (currentNum.length > 16) {
    currentNum = currentNum.slice(0, -1) // この時のCurrentNumはStringです
  }
```

### 小数点を含む場合に正しく計算できない
原因はあまり詳しく調べていないですが、JavaScriptでは小数点を含む場合に正しく計算できないことがあるようで、これに対処するために色々やり方はあるみたいですが、今回はbig.jsというライブラリを使いました。

普通に計算した場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681683/e3353d4e-93e4-5b91-db35-b00570cc0b91.png)

big.jsを使った場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/681683/44b82978-19c9-3346-9cc9-6f04ae0dc39b.png)

アプリでの実装は次のような感じで、[+]や[-]が押されたときにbig.jsのメソッドで計算して、`Number`型に変換しています。

```JavaScript:script.js
if (ope === '+') {
  answer = Big(firstTerm).plus(secondTerm).toNumber()
} else if (ope === '-') {
  answer = Big(firstTerm).minus(secondTerm).toNumber()
} else if (ope === '×') {
  answer = Big(firstTerm).times(secondTerm).toNumber()
} else if (ope === '÷') {
  answer = Big(firstTerm).div(secondTerm).toNumber()
}
```

### キーボード入力で、ボタンをクリックしたときと同じ処理をさせる
例えば、キーボードの[1]を押したときに、画面に表示してある[1]のボタンに登録してあるイベントハンドラを実行させるという実装をしました。`dispatchEvent`メソッドを使って、`click`イベント発生時に実行するイベントハンドラを`keydown`イベント発生時にも実行させています。

```JavaScript:script.js
one.addEventListener('click', appendNum) // oneは[1]ボタン要素への参照。クリック時に[1]を計算式に追加する

// 同じ動作をキーボードの[1]キーが入力されたときに実行したい
window.addEventListener('keydown', getKeyboardValue) // 何らかのキーが押されたときに、getKeyboardValueを実行
const getKeyboardValue = e => {
  switch (e.key) { // 入力されたキーを判断
    case '0':
      zero.dispatchEvent(new Event('click'))
      break
    case '1':
      // 入力されたキーが[1]であれば、'click'イベントが発火したと捉える→appendNumが実行される
      one.dispatchEvent(new Event('click')) 
      break
// 以下、入力されたキーごとに条件分岐
}
```

### type="module"設定時にローカル環境でテストする
今回は`script.js`に外部ライブラリを読み込んでいるので`<script>`の`type="module"`を設定しますが、ローカル環境で普通に`index.html`を開いて...とやると、エラーが出ます。

```html:index.html
<script type="module" src="script.js" defer></script>
```

エラー

```
Access to script at 'file:///path/to/script.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
```

で、これは`file://`でアクセスしているのが原因らしく、`http://`でアクセスしてやればいいらしいです。やり方については、下記のMDNの記事がとても分かりやすいです。pythonでローカルサーバを立ち上げればいいらしい。

https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server

## 参考文献・記事
[fitty](https://github.com/rikschennink/fitty)
[big.js](http://mikemcl.github.io/big.js/)
[How do you set up a local testing server?](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server)
[JavaScript コードレシピ集](https://www.amazon.co.jp/JavaScript-%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AC%E3%82%B7%E3%83%94%E9%9B%86-%E6%B1%A0%E7%94%B0-%E6%B3%B0%E5%BB%B6/dp/4297103680)