1. はじめに
個人的に、何らかサイトの会員登録画面で生年月日を入力するとき、
存在しない月日(例えば 9月31日、2月30日、4月31日 など) が選択できてしまうことが 割とよくあります。
システム的にも、ユーザー的にも、存在しない月日は入力できない方が良いはずです。
さらに、閏年は2月29日までありますし、
今回は、その点も含めて 存在しない年月日を入力できないように セレクトボックスの選択肢を制御する方法を調べてみました。
2. 準備
作成にあたって、以下のツールを準備します。
- テキストエディター
- ブラウザ (ここではChromeを使用)
また、ライブラリーに jQueryを使用します。
jQueryのサイトからファイルの読み込み方を確認します。
- jQuery
https://code.jquery.com/
3. 作成
作成した生年月日フォームのhtml・jsファイルの全体像とブラウザ画面を示します。
3-1. コーディング
以下に作成した html(main.html) と javascript(form.js) を示します。
main.htmlでは、jQueryライブラリーと"js/form.js"を呼び出しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Form</title>
<script
src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
crossorigin="anonymous"></script>
<script type="text/javascript" src="js/form.js"></script>
</head>
<body>
<li>
<label for="birth">年月日: </label>
<select id="year">
<option value="0">----</option>
</select> 年
<select id="month">
<option value="0">--</option>
</select> 月
<select id="date">
<option value="0">--</option>
</select> 日
</li>
</body>
main.htmlで呼び出している、form.jsの内容は以下の通りです。
$(function() {
// 現在の年月日を取得
var time = new Date();
var year = time.getFullYear();
var month = time.getMonth() + 1;
var date = time.getDate();
// 選択された年月日を取得
var selected_year = document.getElementById("year").value;
var selected_month = document.getElementById("month").value;
// 年(初期): 1900〜現在の年 の値を設定
for (var i = year; i >= 1900 ; i--) {
$('#year').append('<option value="' + i + '">' + i + '</option>');
}
// 月(初期): 1~12 の値を設定
for (var j = 1; j <= 12; j++) {
$('#month').append('<option value="' + j + '">' + j + '</option>');
}
// 日(初期): 1~31 の値を設定
for (var k = 1; k <= 31; k++) {
$('#date').append('<option value="' + k + '">' + k + '</option>');
}
// 月(変更):選択された年に合わせて、適した月の値を選択肢にセットする
$('#year').change(function() {
selected_year = $('#year').val();
// 現在の年が選択された場合、月の選択肢は 1~現在の月 に設定
// それ以外の場合、1~12 に設定
var last_month = 12;
if (selected_year == year) {
last_month = month;
}
$('#month').children('option').remove();
$('#month').append('<option value="' + 0 + '">--</option>');
for (var n = 1; n <= last_month; n++) {
$('#month').append('<option value="' + n + '">' + n + '</option>');
}
});
// 日(変更):選択された年・月に合わせて、適した日の値を選択肢にセットする
$('#year,#month').change(function() {
selected_year = $('#year').val();
selected_month = $('#month').val();
// 現在の年・月が選択された場合、日の選択肢は 1~現在の日付 に設定
// それ以外の場合、各月ごとの最終日を判定し、1~最終日 に設定
if (selected_year == year && selected_month == month ) {
var last_date = date;
}else{
// 2月:日の選択肢は1~28日に設定
// ※ ただし、閏年の場合は29日に設定
if (selected_month == 2) {
if((Math.floor(selected_year%4 == 0)) && (Math.floor(selected_year%100 != 0)) || (Math.floor(selected_year%400 == 0))){
last_date = 29;
}else{
last_date = 28;
}
// 4, 6, 9, 11月:日の選択肢は1~30日に設定
}else if(selected_month == 4 || selected_month == 6 || selected_month == 9 || selected_month == 11 ){
last_date = 30;
// 1, 3, 5, 7, 8, 10, 12月:日の選択肢は1~31日に設定
}else{
last_date = 31;
}
}
$('#date').children('option').remove();
$('#date').append('<option value="' + 0 + '">--</option>');
for (var m = 1; m <= last_date; m++) {
$('#date').append('<option value="' + m + '">' + m + '</option>');
}
});
});
3-2. 画面
ブラウザで表示される画面を示します。
例1) 現在の年月日が 2019年11月20日として、ユーザーが 「年」に"2019"、「月」に"11" を選択した場合、日には選択肢として*"1~20"* が表示されます。
(2019年11月21日~2019年11月30日 は選択できない。)
例2) ユーザーが、「年」に"2016"、「月」に"2" を選択した場合、「日」には選択肢として*"1~29"* が設定されます。
See the Pen Form by Haruka Ogawa (@haruka0121) on CodePen.
4. 解説
form.js の処理内容を解説します。
4-1. 現在の年月日取得
セレクトボックスで 未来の年月日を選択できないように処理するため、
まず 現在の年月日を取得しておく必要があります。
**new Date()**で現在の日付オブジェクトを生成し、
そこから 現在の年月日の値をそれぞれ取得して、変数 year、month、dateに入力します。
// 現在の年月日を取得
var time = new Date();
var year = time.getFullYear();
var month = time.getMonth() + 1;
var date = time.getDate();
・日付オブジェクト 生成
**new Date()**を使用し、日付オブジェクトを生成します。
引数に何も指定しない場合、現在の日付オブジェクトが生成されます。
new Date();
今回、生成した日付オブジェクトは、変数timeに入力します。
・現在の年 取得
getFullYear() を使用し、指定した日付オブジェクトの「年」を取得します。
構文は以下の通りです。
日付オブジェクト.getFullYear();
今回は、日付オブジェクトとして 変数time を指定します。
・現在の月 取得
getMonth() を使用し、指定した日付オブジェクトの「月」を取得します。
※ getMonth() は 0~11で返されるため、値に +1 して使用します。
構文は以下の通りです。
// 0から11の値で返される
日付オブジェクト.getMonth()
今回は、日付オブジェクトとして 変数time を指定します。
・現在の日 取得
getDate() を使用し、指定した日付オブジェクトの「日」を取得します。
構文は以下の通りです。
日付オブジェクト.getDate()
今回は、日付オブジェクトとして 変数time を指定します。
4-2. 選択された年月日取得
セレクトボックスで 存在しない日 や 未来の年月日 を選択できないようにするため、
「年」・「月」セレクトボックスで ユーザーが選択した値によって、「月」・「日」セレクトボックスの選択肢を設定します。
まず ここでは、「年」・「月」セレクトボックスで ユーザーが選択した値を取得します。
getElementByIdを使用し、ユーザーが選択した「年」・「月」の値を取得し、
それぞれ変数 selected_year、selected_monthに代入します。
// 選択された年月日を取得
var selected_year = document.getElementById("year").value;
var selected_month = document.getElementById("month").value;
引数には、htmlで設定した「年」・「月」セレクトボックスのid名("year"・"month")を指定します。
構文は以下の通りです。
document.getElementById(id).value;
4-3. 年月日の選択肢設定(初期)
フォーム画面が表示された時点(初期)での、「年」「月」「日」セレクトボックスの選択肢を設定します。
・「年」の選択肢設定(初期)
「年」セレクトボックスは、1900~現在の年 の値を選択肢とします。
現在の年 は、変数 year に代入してあります。
// 年(初期): 1900〜現在の年 を設定
for (var i = year; i >= 1900 ; i--) {
$('#year').append('<option value="' + i + '">' + i + '</option>');
}
ループ処理を用いて、1900 ~ year の値を「年」の選択肢として生成します。
生成した値は、**append()**を用いて、「年」セレクトボックスに挿入します。
$(セレクタ).append(挿入コンテンツ);
ここでは、セレクタとして htmlで指定した「年」セレクトボックスの id名("year")を指定します
・「月」の選択肢設定(初期)
「月」セレクトボックスは、1~12 の値を選択肢とします。
// 月(初期): 1~12 の値を設定
for (var j = 1; j <= 12; j++) {
$('#month').append('<option value="' + j + '">' + j + '</option>');
}
ループ処理を用いて、1~12 の値を「月」の選択肢として生成します。
生成した値は、**append()**を用いて、「月」セレクトボックスに挿入します。
セレクタとして htmlで指定した「月」セレクトボックスの id名("month")を指定します
・「日」の選択肢設定(初期)
「日」セレクトボックスは、1~31 の値を選択肢とします。
// 日(初期): 1~31 の値を設定
for (var k = 1; k <= 31; k++) {
$('#date').append('<option value="' + k + '">' + k + '</option>');
}
ループ処理を用いて、1~31 の値を「日」の選択肢として生成します。
生成した値は、**append()**を用いて、「日」セレクトボックスに挿入します。
セレクタとして htmlで指定した「日」セレクトボックスの id名("date")を指定します
4-4. 月日の選択肢 変更
・「月」の選択肢 変更
未来の年月日が選択されないように、月の選択肢を変更します。
例えば、現在が2019年11月21日の場合、
ユーザーが「年」セレクトボックスで2019を選択したとき、「月」セレクトボックスの選択肢に 12 が表示されないようにします。
// 月(変更):選択された年に合わせて、適した月の値を選択肢にセットする
$('#year').change(function() {
// 選択された年の値を取得する
selected_year = $('#year').val();
// 現在の年が選択された場合、月の選択肢は 1~現在の月 に設定
// それ以外の場合、1~12 に設定
var last_month = 12;
if (selected_year == year) {
last_month = month;
}
$('#month').children('option').remove();
$('#month').append('<option value="' + 0 + '"></option>');
for (var n = 1; n <= last_month; n++) {
$('#month').append('<option value="' + n + '">' + n + '</option>');
}
});
- change():「月」セレクトボックス 選択肢 変更処理 実行
**change()**を用いて、
「年」セレクトボックスで値が選択された時に、
「月」セレクトボックス 選択肢 変更処理が実行されるように設定します。
$(セレクタ).change(ファンクション)
ここでは、セレクタとして htmlで指定した「年」セレクトボックスの id名("year")を指定します。
ファンクションには、「月」セレクトボックスの選択肢を変更する処理を記述します。
- val() :「年」セレクトボックスの選択された値取得
**val()**を用いて、
選択された「年」の値を取得します。
$(セレクタ).val()
ここでは、セレクタとして htmlで指定した「年」セレクトボックスの id名("year")を指定します。
- 「月」選択肢 の値範囲 決定
条件処理を用いて、
生成する「月」セレクトボックスの選択肢の値の範囲を決定します。
「年」セレクトボックスで
現在の年 が選択された場合、「月」セレクトボックスの選択肢は 1 ~ 現在の月 、
それ以外の場合、「月」セレクトボックスの選択肢は 1~12
に設定します。
- remove() :「月」セレクトボックスの選択肢 削除
**remove()**を用いて、
設定されている「月」セレクトボックスの選択肢の値を削除し、初期化します。
$(セレクタ).remove();
- append() :「月」セレクトボックス 未選択時 の値 挿入
**append()**を用いて、
「月」セレクトボックス 未選択時 の値 を挿入します。
$(セレクタ).append(挿入コンテンツ);
ここでは、セレクタとして htmlで指定した「月」セレクトボックスの id名("month")を指定します。
挿入コンテンツには、「月」セレクトボックス未選択時 の値として*"--"*が 表示されるように設定します。
- 「月」セレクトボックス の選択肢の値 生成・変更
ループ処理を用いて、「月」の選択肢の値を生成します。
生成した値は**append()**を用いて、「月」セレクトボックスに挿入します。
・「日」の選択肢 変更
存在しない年月日が選択されないように、「日」セレクトボックスの選択肢を変更します。
具体的には、
現在の年月日を確認し、未来の年月日が選択できないよう、「日」セレクトボックスの選択肢を変更します。
さらに、閏年や各月の正しい最終日を考慮し、存在しない日(9月31日、2月30日、4月31日 など )も選択できないように処理します。
// 日(変更):選択された年・月に合わせて、適した日の値を選択肢にセットする
$('#year,#month').change(function() {
selected_year = $('#year').val();
selected_month = $('#month').val();
// 現在の年・月が選択された場合、日の選択肢は 1~現在の日付 に設定
// それ以外の場合、各月ごとの最終日を判定し、1~最終日 に設定
if (selected_year == year && selected_month == month ) {
var last_date = date;
}else{
// 2月:日の選択肢は1~28日に設定
// ※ ただし、閏年の場合は29日に設定
if (selected_month == 2) {
if((Math.floor(selected_year%4 == 0)) && (Math.floor(selected_year%100 != 0)) || (Math.floor(selected_year%400 == 0))){
last_date = 29;
}else{
last_date = 28;
}
// 4, 6, 9, 11月:日の選択肢は1~30日に設定
}else if(selected_month == 4 || selected_month == 6 || selected_month == 9 || selected_month == 11 ){
last_date = 30;
// 1, 3, 5, 7, 8, 10, 12月:日の選択肢は1~31日に設定
}else{
last_date = 31;
}
}
$('#date').children('option').remove();
$('#date').append('<option value="' + 0 + '">--</option>');
for (var m = 1; m <= last_date; m++) {
$('#date').append('<option value="' + m + '">' + m + '</option>');
}
});
- change():「日」セレクトボックス 選択肢 変更処理 実行
**change()**を用いて、
「年」・「月」セレクトボックスで値が選択された時に、
選択肢 変更処理が実行されるように設定します。
$(セレクタ).change(ファンクション)
ここでは、セレクタとして htmlで指定した「年」・「月」セレクトボックスの id名("year", "month")を指定します。
ファンクションには、「日」セレクトボックスの選択肢を変更する処理を記述します。
- val() :「年」・「月」セレクトボックスの選択された値取得
**val()**を用いて、
選択された「年」・「月」の値を取得します。
$(セレクタ).val()
ここでは、セレクタとして htmlで指定した「年」・「月」セレクトボックスの id名("year", "month")を指定します。
- 「日」選択肢 の値範囲 決定
条件処理を用いて、
生成する「日」セレクトボックスの選択肢の値の範囲を決定します。
ここでは、「年」・「月」セレクトボックスで、
それぞれ 現在の年・月 が選択された場合、「日」セレクトボックスの選択肢は 1~現在の日付 、
それ以外の場合、「月」セレクトボックスの選択肢は 1~月の最終日
に設定します。
月の最終日は、閏年や月で異なるため、
さらに細かく条件を分岐させて値の範囲を設定します。
選択された「月」の値が2月の場合、
「日」セレクトボックスの選択肢の範囲は
閏年であれば1~29日、
閏年ではなければ1~28日に設定します。
※ 以下の場合、閏年と判定する。
・西暦を4で割り切れ
て、かつ100で割り切れない
・西暦を400で割り切れる
選択された「月」の値が4, 6, 9, 11月の場合、
「日」セレクトボックスの選択肢の範囲は 1~30日に設定します。
選択された「月」の値が1, 3, 5, 7, 8, 10, 12月の場合、
「日」セレクトボックスの選択肢の範囲は 1~31日に設定します。
- remove() :「日」セレクトボックスの選択肢 削除
**remove()**を用いて、
設定されている「日」セレクトボックスの選択肢の値を削除し、初期化します。
$(セレクタ).remove();
- append() :「日」セレクトボックス 未選択時 の値 挿入
**append()**を用いて、
「日」セレクトボックス 未選択時 の値 を挿入します。
$(セレクタ).append(挿入コンテンツ);
ここでは、セレクタとして htmlで指定した「日」セレクトボックスの id名("date")を指定します。
挿入コンテンツには、「日」セレクトボックス未選択時 の値として*"--"*が 表示されるように設定します。
- 「日」セレクトボックス の選択肢の値 生成・変更
ループ処理を用いて、「日」の選択肢の値を生成します。
生成した値は**append()**を用いて、「日」セレクトボックスに挿入します。
5. おわりに
今回は、存在しない年月日が入力できない 閏年判定付き 生年月日フォームを作成しました。
jQueryを使用すると、簡単にhtml要素を変更できるので、生年月日フォーム以外でも活用できそうです。
追記
「年」の値が選択されたら「月」、
「年」・「月」の値が選択されたら「日」 の選択肢が再設定される処理の流れになっていますが、
ユーザーが「年」より先に「月」or「日」、または「月」より先に「日」の値を選択していた場合、
選択肢の再設定処理で、ユーザーの選択していた値が初期化されてしまいます。
もっと理想を言えば、再設定処理がされても、ユーザーが選択していた値を保持できれば 良いなと思います。
参考情報
・JavaScript 日本語リファレンス
http://js.studio-kingdom.com/
・nkmrkisk.com:【js】jQuery or jsで生年月日入力formを自動生成するサンプルコード
https://nkmrkisk.com/archives/1153
・yuya4の備忘録:【JavaScript】【jquery】セレクトボックスでのうるう年計算
http://yu-ya4.hatenablog.com/entry/2015/07/03/195409
・ITの隊長のブログ:【jQuery】年と月からうるう年も計算した日を出力する
https://www.aipacommander.com/entry/2015/08/11/155210
・jQuery CDN – Latest Stable Versions
https://code.jquery.com/