『閏年判定アプリ』を作った

<h2>まえがき</h2>

こんにちは、清掃屋です。
2020年はコロナの影響により排水管清掃業も大きな影響を受け4～7月頃まで暇な時間が増えました。
その時期にかねてより興味があったIT関係の勉強をチマチマ始め、現在は勉強を始めて1年3か月ほどになります。

「<b>そろそろ教科書をなぞるだけではなく、何か自作物を作ってみよう</b>」という意気込みで、今回「閏年判定アプリ」を作りましたので。その結果をここに残します。

製作時間は6時間です。

<h2>もくじ</h2>

[1. 目的](#1-目的)

[2. 完成物](#2-完成物)

[3. 制作の流れ](#3-制作の流れ)

[4. ふりかえり](#4-ふりかえり)


##1. 目的

いきなり難しいものは作らず、一つの機能のみを備えた簡単なアプリをつくることにした。
アプリの内容にはこだわらず、<b>基本的な処理が出来ることを制作の目的</b>とした。

基本的な処理とは
<b>「ユーザーが入力し、ボタンを押すと処理が実行され、結果が表示される。」</b>
とする。

そして、<b>このアプリの肝は正規表現による入力チェックになります。</b>

##2. 完成物

<a id="anchor2"></a>
完成物の見た目は以下のようなものになります
![ifjm.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/a4f53405-2425-bc94-e276-5b02ecd93cfc.jpeg)
![iugui.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/3ed7fc7a-0ca1-0c9b-d1a5-7c29d8374e94.jpeg)
<b>1:　4桁までの半角数字しか入力できません。</b>
<b>2:　4桁の数字が閏年でなければ「普通の年」と表示されます。</b>
<b>3:　4桁の数字が閏年ならば「閏年です」と表示されます。</b>
![r76i1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/05effcba-eb7a-a099-f57d-965a32079e6a.jpeg)
<b>4:　未入力でボタンを押した場合は「未入力」と表示されます。</b>
<b>5:　4桁未満でボタンを押した場合は「4桁で！」と表示されます。</b>
<b>6:　リセットボタンを押すと入力された値がリセットされます。</b>





##3. 制作の流れ

<a id="anchor3"></a>
ここからは完成物が完成するまでの流れを振り返ります。
まず、この『閏年判定アプリ』の基本的な処理は3つの部品で構成されている。
<ul>
[<li>入力欄](#入力欄)
[<li>処理実行のボタン](#処理実行のボタン)
[<li>結果の出力](#結果の出力)
</ul>
私は「処理実行のボタン」から制作することにした。
「入力欄」と「結果の出力」は後で簡単に追加できると考えたからだ。

##処理実行のボタン

<h3>処理部分について</h3>
閏年の判定は
「4で割り切れる　なおかつ　１００で割り切れない　あるいは　４００で割り切れる」
つまりこんな形になる
`year%4 ===0 && year%100 !== 0 || year%400 === 0`
<b>これが正しいかどうかまずテストしてみることにした。</b>

```html
<!DOCTYPE HTML>
<HTML>
  <head>
    <meta charset ="UTF-8">
  </head>
  <body>
    <script>
      function urudosi(year){
        if(year%4 ===0 && year%100 !== 0 || year%400 === 0){
          return true;
        }else{
          return false;
        }
      }

      for(let i = 1500; i < 2100; i++ ){
        if(urudosi(i)){
          console.log(i +'閏年です');
        }else{

        }
      }
    </script>
  </body>
</HTML>
```
コレを実際の閏年の資料と比較して合っているかどうか確認する。
100で割り切れる値の内、400で割り切れるものだけが出力されていれば大丈夫でしょ。

大丈夫そうなのでその後、この処理を以下のように書き直した。

```html
<!DOCTYPE HTML>
<HTML>
  <head>
    <meta charset ="UTF-8">
  </head>
  <body>
    <script>
      const year = 2021;
      if(year%4 ===0 && year%100 !== 0 || year%400 === 0){
        console.log('閏年です');
      }else{
        console.log('閏年ではありません');
      }
    </script>
  </body>
</HTML>
```

今回は関数に渡す値を2021にしているので「閏年ではありません」と出力されるはず。
![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/ae71272b-6971-ddeb-f4c3-e85bcd484f5b.jpeg)
とりあえずOKそうなので次へ進みます。

<h3>ボタンと連動</h3>

次はボタンを作る。
ボタンを押すとaddEventListenerでclickした場合に先ほど作った処理部分が実行されるようにする。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset = "UTF-8">
    <title>閏年チェッカー</title>
  </head>
  <body>

    <div id = "button">実行</div>

    <script src = "main.js"></script>
  </body>
</html>
```

```javascript
'use strict';

  const urudosiHantei = document.getElementById('button');
  urudosiHantei.addEventListener('click', () => {
    
    const year = 2020;
     if(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0){
        urudosiHantei.textContent = '閏年です';
      } else {
        urudosiHantei.textContent = '閏年ではありません';
      }
    });
```

今回はyearに2020を渡しているので結果は「閏年です」が出力されるはず。

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/6c0743c2-6699-83d7-518b-08945334d331.jpeg)
![ｋｇ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/67dd8988-c826-d69e-35dd-44f33cab46d2.jpeg)
大丈夫そうですね。後はCSSでそれっぽく整形するだけです。
整形したものが下にあります。
![srh.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/24ff5d39-cf30-fe81-648e-a7354d7dd64f.jpeg)
CSSは省略させてください。長いので…
これで「処理実行ボタン」は完成です。


##結果の出力

何となくボタンに結果を出力してしまっているが
本来は結果を出力する専用のアイコンなどを用意しようと考えていた。

しかし、制作の目的が<b>「基本的な処理が出来ること」</b>なので<b>これでOKと判断しました。</b>

ちなみに文字サイズの関係で4文字しか綺麗に表示できません。
その為、<b>「閏年ではありません」の時は以下のように表示を改めました。</b>
![oi.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/a1ec9ae6-33a4-ba8f-051e-5efca702a1e5.jpeg)
「閏年ではありません」の時だけ、文字サイズを変更させようかと考えましたが、制作の目的にそぐわないのでやめました。
<b>まずは完成させることが第一だ！</b>


##入力欄

入力欄は`input type = "text" `を使うことにした。
`input type = "number" `の方が数字しか入力できないので良いように感じますが
![455.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/56fbdb8c-d176-99b7-c455-223f90b80573.jpeg)
` type = "number" `の場合`maxlength`が効かず4桁を超えて入力できてしまいます。またスピンボタンも出てしまいます。
スピンボタンは簡単に消せるし、4桁以上入力してもJavaScriptの入力制限で処理を弾くこともできますが、入力できてしまうことがなんか嫌なので、今回は`input type = "text" `を使います。

入力欄には<b>「半角数字4文字」</b>を入力してほしいので

```html
<input type = "text" id = "inputYear" maxlength = "4" 
oninput = "value = value.replace(/[^0-9]+/i,'');" >
```

となりました。ここで

```html
oninput = "value = value.replace(/[^0-9]+/i,'');"
```

の箇所に関しては<b>下記のサイト様の記事を参考にしました。</b>

[<b>\<input type="text">で数値のみ入力する方法</b>](https://confrage.jp/input-typetext%E3%81%A7%E6%95%B0%E5%80%A4%E3%81%AE%E3%81%BF%E5%85%A5%E5%8A%9B%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)

>参考にした箇所の説明としては、`oninput`は`input`要素の値が変更された際に実行されるイベントハンドラで`replace`により半角数字以外の文字が入力された際に`''`内の値で置き換えてくれるようです。
>今回の場合は、削除してくれます。

なお、半角数字を入力後に全角で入力すると2文字分削除されるらしく、入力した半角数字が1文字づつ消されます。
普通の操作ではないし、大きな問題ではないので放置します。

<h3>入力欄から渡された値の処理

ボタンが押されたら入力欄の値を受け取って定数yearとして処理に渡す。

```javascript
  const urudosiHantei = document.getElementById('button');
  urudosiHantei.addEventListener('click', () => {
    
    const inputY = document.getElementById("inputYear");
    const year = inputY.value;
```

そして入力された値が制限に反していないかチェックする。

現状、4文字以下の半角数字しか入力できない状態なので、チェックすべきは<b>「4文字未満」</b>の状態かどうかであると考えた。
普通にmatch()を使って半角数字4文字かどうか確認させます。その結果が違っていたらmutch()はnullを返します、JavaScriptではnullがif文に入っていた時にfalseを返すので、反転させてtrueにします。nullでなけばfalseを返すのでそのまま閏年の判定に移行します。

```javascript
      if(!year.match(/[0-9]{4}/)){  
        urudosiHantei.textContent = '4桁で！';
      } else if(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0){  /* 閏年の判定 */
           urudosiHantei.textContent = '閏年です';
         } else {
           urudosiHantei.textContent = '普通の年';
          }    
```

このままでも良いのですが、<b>未入力状態でボタンを押した場合も「4桁で！」と表示されます。</b>
これはちょっとズレた反応のように感じたので、一番最初に未入力かどうか判定させることにしました。

先ほど説明した通り、JavaScriptだとif文の中身が空だった場合falseを返すので反転させてtrueにします。

```javascript
    if(!year){  /* yearの中身が無かった場合falseになる、それを反転させる */
      urudosiHantei.textContent = '未入力';
    } else if(!year.match(/[0-9]{4}/)){/* yearが数字4桁でなければnullを返す、falseになるので反転させてtureを返す */
        urudosiHantei.textContent = '4桁で！';
      } else if(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0){  /* 閏年の判定 */
           urudosiHantei.textContent = '閏年です';
         } else {
           urudosiHantei.textContent = '普通の年';
          }   
```
これで未入力の場合には、ちゃんと「未入力」と判定されてくれます。


<h2>部品追加

現状こんな感じである。
![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/3764a1ae-ac8d-9bc6-2804-a0cfaa0cd9d9.jpeg)
使ってみて分かったが、値を入力した後に入力欄の値を消すか、上書きしないと次の判定が出来ない。
これは面倒くさいのでリセットボタンを作ることにした。

普通のリセットボタンです。

```html
      <div id = "reset" >リセット</div>
```

```javascript
  const resetBotton = document.getElementById('reset');
  resetBotton.addEventListener('click', () => {
    const textReset = document.getElementById("inputYear");
    textReset.value = '';
  });
```

リセットボタンを押されたら入力欄の値を消すだけの処理です。
![tyyt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820236/4eeebf38-db7f-c982-0052-ead1e63e9771.jpeg)
これで完成！ダサい…


##4. ふりかえり

<a id="anchor4"></a>
閏年を調べるだけのアプリという最低限のものは作れたと思う。
よって<b>基本的な処理が出来ることを制作の目的にする</b>という目標は達成できたと思います。
来年、この記事を見たら恥ずかしくて消したくなるよう成長したいです。

<b>一番の肝である入力チェックについてはほぼ理想的な動作をしてくれているので満足です。</b>

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset = "UTF-8">
    <title>閏年チェッカー</title>
    <link rel = "stylesheet" href = "styles.css">
  </head>
  <body>
    <!--閏年判定アプリの親要素-->
    <div class = "relative">

      <!--リセットボタン-->
      <div id = "reset" >リセット</div>

      <!--入力欄の単位表示用-->
      <p id = "unitYear">年</p>

      <!--入力欄、文字数制限、入力制限-->
      <input type = "text" id = "inputYear" maxlength = "4" oninput = "value = value.replace(/[^0-9]+/i,'');" >
      
      <!--入力欄の説明用-->
      <p id = "textEX">4桁の半角数字を入力して下さい</p>

      <!--実行＆判定表示ボタン-->
      <div id = "button">判定</div>
    
    </div>
    <script src = "main.js"></script>
  </body>
</html>
```

```javascript
'use strict';

  //リセットボタンの処理
  const resetBotton = document.getElementById('reset');
  resetBotton.addEventListener('click', () => {
    const textReset = document.getElementById("inputYear");
    textReset.value = '';
  });

  //実行＆判定表示ボタンの処理
  const urudosiHantei = document.getElementById('button');
  urudosiHantei.addEventListener('click', () => {
    
    const inputY = document.getElementById("inputYear");
    const year = inputY.value;

    if(!year){  /* yearの中身が無かった場合falseになる、それを反転させる */
      urudosiHantei.textContent = '未入力';
    } else if(!year.match(/[0-9]{4}/)){/* yearが数字4桁でなければnullを返す、falseになるので反転させてtureを返す */
        urudosiHantei.textContent = '4桁で！';
      } else if(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0){  /* 閏年の判定 */
           urudosiHantei.textContent = '閏年です';
         } else {
           urudosiHantei.textContent = '普通の年';
          }    
  });
```