【jQuery】日付自動生成型生年月日フォームを作る（うるう年判定含む）

# 導入
問い合わせフォームや会員登録フォームで、生年月日を入力するプルダウン、よくありますよね。

その中で、月の末日というのは、「3月は31日まで」「9月は30日まで」「2月はうるう年なら29日まで」など、年月によって変わってきます。

存在しない日付は、最初から選択肢に無いほうが望ましいですよね。

ということで今回は、選択された「年月」から、選択可能な「日」を自動生成してくれる、さらにはうるう年も考慮にいれた、いい感じのフォームをjqueryで実装します。

# 1. フォームを作る

最初にシンプルなフォームを作ります。

```html
<form action="./confirm.php" method="POST" enctype="multipart/form-data”>
         <table>
           <tr>
              <th>生年月日のフォーム</th>
              <td>
                <div class="flex-container">
                  <select id="year" name="year" class="field-year">
                  </select>
                  <label>年</label>
                  <select id="month" name="month" class="field-year">
                  </select>
                  <label>月</label>
                  <select id="day" name="day" class="field-year">
                  </select>
                  <label>日</label>
                </div>
              </td>
              </tr>
            <tr>
        </table>
<input type="submit" value="送信" class="btn btn-blue">
 </form>
```


セレクトボックスの中身は、以下のように\<option>を一つずつ書くこともできますが、たとえば今回のように1970年から2022年だと、コードがとんでもなく長くなってしまいます。


```html
<option value=“1970”>1970</option>
<option value=“1971”>1971</option>
<option value=“1972”>1972</option>
```


なので、選択肢を生成する関数を新たに作ります。


```js
//年の選択肢生成
  var createYearOption = (startNum, endNum, current) => {
      let optionDom = '';
      for (let i = startNum; i <= endNum; i++) {
        if (i === current) { //現在年と合致する場合は初期値にする
          option = '<option value="' + i + '" selected>' + i + '</option>';
        } else {
          option = '<option value="' + i + '">' + i + '</option>';
        }
        optionDom += option;
      }
      $("#year").append(optionDom);
    }
```

optionの先頭と最後のvalue、現在の年を引数に取り、指定した個数分、\<option>を生成します。
生成された\<option>は、optionDomに追加されていき、最後の\<option>まで追加されたら、セレクトボックスの中にinsertします。

同様に「月」と「日」の関数も作ります。


```js
//月の選択肢生成
    var createMonthOption = (startNum, endNum, current) => {
      let optionDom = '';
      for (let i = startNum; i <= endNum; i++) {
        if (i === current) {
          option = '<option value="' + i + '" selected>' + i + '</option>';
        } else {
          option = '<option value="' + i + '">' + i + '</option>';
        }
        optionDom += option;
      }
      $("#month").append(optionDom);
    }

 //日の選択肢生成
    var createDayOption = (startNum, endNum, current) => {
      let optionDom = '';
      for (let i = startNum; i <= endNum; i++) {
        if (i === current) {
          option = '<option value="' + i + '" selected>' + i + '</option>';
        } else {
          option = '<option value="' + i + '">' + i + '</option>';
        }
        optionDom += option;
      }
      $("#day").append(optionDom);
    }
```

あとはこれをページに読み込むと同時に呼び出してあげます。
その前に現在日時も取得しておきましょう。
ちなみに、getMonth()に1を足しているのは、getMonth()の戻り値が、1月の場合は0、2月の場合は1というように、現在月-1の数値であるためです。



```js
//今日の日付、年、月、日　を取得
    var today = new Date();
    var thisYear = today.getFullYear();
    var thisMonth = today.getMonth() + 1;
    var thisDate = today.getDate();
    var thisMonthDay = 31; 

//作った関数を呼び出す
   $(document).ready(function(){    
      createYearOption(1970,thisYear, thisYear);
      createMonthOption(1,12,thisMonth);
      createDayOption(1,thisMonthDay,thisDate);
    });
```

これでとりあえず、生年月日を選択できるセレクトボックスの完成です。


![simple_form_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1175390/3918b108-4d88-2a4d-d905-b57e83ce2194.png)

しかし、このフォームには問題があります。
「2月30日」や「9月31日」などの存在しないであろう日付を選べてしまうのです。うるう年の影響も考慮しなくてはなりません。



# 2. うるう年判定関数を作る
次に、うるう年かどうかを判定する関数を作っておきます。
うるう年の定義は以下の通りです。

>1.西暦年号が4で割り切れる
2.1の例外として、西暦年号が100で割り切れて400で割り切れない年は平年とする



これを踏まえて作った関数が以下になります。

```js
//うるう年判定の関数
  function isLeapYear(year){
    if( (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
      return true;
    } else{
      return false;
    }
  }
```


はい、これで引数に数字を入れて、うるう年ならtrue、うるう年じゃないならfalseを返す関数ができました。


# 3. フォームに組み込む

年、または月が選択されたとき、それに対応する日数を判定する処理を作ります。

流れとしては、以下のような条件分岐で日数を判定し、それを先程作った、日付を生成してくれる関数createDayOption(1,thisMonthDay,thisDate);
のthisMonthDayに入れるといった感じです。


![routing-map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1175390/aee412f1-27e5-bf10-d366-2555ce893f46.png)


```js
$("#year").change(function(){  //年が選択されたとき
      var selected_year = $(this).val();       //現時点の年に入っている値を取得
      var selected_month = $('#month').val();  //現時点の月に入っている値を取得
      
      var month_days = [0,31,28,31,30,31,30,31,31,30,31,30,31];//各月の日数
      thisMonthDay = month_days[selected_month];
     
      if(selected_month == 2 && isLeapYear(selected_year) == true){  //うるう年かつ2月のとき
           thisMonthDay = 29;    
        } 

      $("#day").empty();
      createDayOption(1, thisMonthDay, 1);
    });
```

change()はselect要素の中身が変更された際にイベント処理を実行する関数なので、「年が選択されたとき」に処理が行われます。

同じように「月が選択されたとき」の処理も書きます。

```js
$("#month").change(function(){  //月が選択されたとき
      var selected_year = $('#year').val();
      var selected_month = $(this).val();
      
      var month_days = [0,31,28,31,30,31,30,31,31,30,31,30,31];//各月の日数
      thisMonthDay = month_days[selected_month];

     if(selected_month == 2 && isLeapYear(selected_year) == true){  //うるう年かつ2月のとき
           thisMonthDay = 29;   
        } 

      $("#day").empty();
      createDayOption(1, thisMonthDay, 1);
    });
```
これで、年または月を選択すると、それに対応した日付の選択肢が反映される（うるう年も考慮された）セレクトボックスが完成しました！

![LearYear.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1175390/fe768df8-4628-0c00-889d-edbc6560ebec.png)

# おわりに
いかがだったでしょうか。
思った以上に長いコードになってしまったので、リファクタリングの余地が大いにある気がしますが、個人的には今後フォームを作るときに迷わなくて済みそうです。
よかったら是非参考にしてみてください！


# 参考資料
みみぺんブログ:https://blog.mimipen.net/entry/2019/09/02/083834