Datepicker で minDate/maxDate を変更したときに勝手に日付を調整させない
jQuery UI の Datepicker で期間を指定するとき、終了日を設定した状態で、開始日をそれより前に変更すると、終了日が勝手に変更されるという現象が発生しました。
例えば、
- 終了日を6月28日
- 開始日を6月29日
という順に指定すると、終了日が勝手に6月30日になってしまいます。
datepicker の例では開始日のカレンダーが、終了日までしか選択できないようになっていますが、キー入力することで同様の現象が発生します。
これは、開始日の change イベントで、終了日の datepicker の最小日(minDate)を6月30日に変更する処理をしているためです。
この挙動がとってもいやで、勝手に変えないようにしたかったので調べました。
jquery-ui.js
を見てみると Datepicker
クラスの _restrictMinMax
メソッドが見つかりました。
/* Ensure a date is within any min/max bounds. */
_restrictMinMax: function( inst, date ) {
var minDate = this._getMinMaxDate( inst, "min" ),
maxDate = this._getMinMaxDate( inst, "max" ),
newDate = ( minDate && date < minDate ? minDate : date );
return ( maxDate && newDate > maxDate ? maxDate : newDate );
},
これを呼び出している _adjustInstDate
や、その前の _setDate
でも、_restrictMinMax
を呼ばないような条件文はないので、_restrictMinMax
自体をオーバーライドすることにしました。
/* override */
$.datepicker._restrictMinMax = function(inst, date){
return date;
};
jquery-ui.js はいじらずに、上記コードを datepicker を作っている JS に書いておけばOKです。
これで勝手に日付を変更されなくなります。
ちょっと動作がわかりにくいと思うので、サンプルを書いてみました。
下記コードを html ファイルにしてブラウザで開くと、「override?」チェックボックスが、チェックされていなければデフォルトの勝手に変更するモード、チェックすればオーバーライドした勝手に変更されないモードになります。
開始日を終了日の後に変更すると、チェックなしは終了日が開始日の翌日に変更され、チェックありは「NG」が表示されます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jQuery UI Datepicker - Select a Date Range</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.3/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>
<script>
$(function() {
const DATE_FORMAT = "yy-mm-dd";
const from = $("#from")
.datepicker({
dateFormat: DATE_FORMAT,
minDate: '0',
})
.on("change", function() {
let today = new Date();
today.setHours(0,0,0,0);
let from_date = getDate(from);
if (from_date) {
if (from_date < today) {
from[0].setCustomValidity("NG");
} else {
from[0].setCustomValidity("");
}
}
updateFromMessage();
let to_min = from_date? from_date : today;
to_min.setDate(to_min.getDate() + 1);
to.datepicker("option", "minDate", to_min);
to.datepicker("option", "defaulDate", to_min);
let to_date = getDate(to);
if (to_date && to_date < to_min) {
to[0].setCustomValidity("NG");
} else {
to[0].setCustomValidity("");
}
updateToMessage();
});
var to = $("#to")
.datepicker({
dateFormat: DATE_FORMAT,
minDate: '1d',
})
.on("change", function() {
let today = new Date();
today.setHours(0,0,0,0);
let to_min = getDate(from);
if (!to_min) {
to_min = today;
}
to_min.setDate(to_min.getDate() + 1);
let to_date = getDate(to);
if (to_date) {
if (to_date < to_min) {
to[0].setCustomValidity("NG");
} else {
to[0].setCustomValidity("");
}
}
updateToMessage();
});
function getDate(element) {
var date;
try {
date = $.datepicker.parseDate(DATE_FORMAT, element.val());
} catch(error) {
date = null;
}
return date;
}
function updateFromMessage() {
if (from[0].checkValidity()) {
$("#from_mess").text("");
} else {
$("#from_mess").text(from[0].validationMessage);
}
};
function updateToMessage() {
if(to[0].checkValidity()){
$("#to_mess").text("");
} else {
$("#to_mess").text(to[0].validationMessage);
}
};
/* override to don't adjust any date */
const original_restrictMinMax = $.datepicker._restrictMinMax;
const dont_restrictMinMax = function(inst, date){
return date;
};
$("#is_override_restrictMinMax").on("change", function() {
$.datepicker._restrictMinMax
= this.checked? dont_restrictMinMax : original_restrictMinMax;
});
});
</script>
</head>
<body>
<form onsubmit="return false;">
<div>
<label for="from">From</label>
<input type="search" id="from" name="from" autocomplete="off" required>
<span id="from_mess" style="color:red;"></span>
</div>
<div>
<label for="to">to</label>
<input type="search" id="to" name="to" autocomplete="off" required>
<span id="to_mess" style="color:red;"></span>
</div>
<input type="checkbox" id="is_override_restrictMinMax">override?</input>
</form>
</body>
</html>
そもそも、datepicker の例でもそうですが、期間指定する際、開始日のカレンダーの maxDate を終了日までしか選択できなくするようなインターフェースは腐っていると思います。
例えば、ホテルの予約を入れるとき、datepicker で7/10~7/11を選んだあとで、実は8/10~8/11だと気づき変更しようとすると、開始日を8/10に変更できないっておかしいですよね。
このようなケースでは終了日から修正すれば変更できる、とか言い出すやつは蹴ってあげてください。