DatePicker

【jQuery UI】Datepicker にボタン追加のカスタマイズ方法

・jQuery UI Datepicker 任意のボタン追加方法
・追加ボタンのイベントハンドリング

業務でDatepickerを使う機会があり色々調べていくうちに使えるか微妙ですが、こういったことも出来るという意味でアウトプットしたいと思います。

Ruby on Railsにてbootstarp導入直後のランタイムエラー解決方法の紹介

はじめに

DatepickerはBootstrapとかAngularJSとか色々あるようですが今回紹介するのはjQuery UIのDatepickerです。

Datepicker
https://jqueryui.com/datepicker/

環境情報

名前詳細
jQuery3.6.0(https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
jQuery UI1.12.1(https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
jQuery UI CSS1.12.1(https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css
エディタJSFiddle(https://jsfiddle.net/

まっさらな状態

カスタマイズを施していない状態は下記となります。
※JSFiddleでは外部JS,CSSは設定でインポートされているためScriptタグやLinkタグは省略しています。

Date Picker on input field: <input type="text" id="date1" name="date1"/>
$('#date1').datepicker({
    changeMonth: true,
    changeYear: true,
    showButtonPanel: true,
    dateFormat: "m/d/yy"
});

デフォルトのDatepickerを呼ぶ場合は上記コードのみです。
それだけで画像のようなカレンダーが表示されてユーザーにとって日付入力が直感的になります。

これだけでも使いやすいのですが、例えば「1年前の今日」や「日付が不明の場合」をボタン1つで入力したい場合は既存のボタンだけでは難しいため後述で紹介します。

ボタン追加方法

HTMLに変更はありません。
jQueryの中で「beforeShow」イベントと「onChangeMonthYear」イベントでButtonタグの追加を行います。
同時にButtonタグに対するクリックイベントも設定します。
(クリックイベントについては後述しますが今回はDatepickerが非表示になる処理を入れています)

「beforeShow」イベントと「onChangeMonthYear」イベントの2つに追加する理由としては、表示前イベントと年月が変わるイベントの2つでレンダリングしているため2つのイベントに対してButtonタグを追加する必要があります。

Date Picker on input field: <input type="text" id="date1" name="date1"/>
$('#datepicker').datepicker({
  changeYear : true,
  changeMonth : true,
  showButtonPanel : true,
  beforeShow : function(input) {
    setTimeout(function() {
      var buttonPane = $(input).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
          $(input).datepicker('hide');
        }
      }).appendTo(buttonPane).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  },
  onChangeMonthYear: function(year, month, instance) {
    setTimeout(function() {
      var buttonPane = $(instance).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
          $('#datepicker').datepicker('hide');
        }
      }).appendTo( buttonPane ).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  }
});

このような形でボタンが追加され、追加したボタンを押すとDatepickerが非表示になります。
ボタンの位置はCSSを設定すればある程度制御可能です。

追加したボタンを対象にイベント

ここからは追加したボタンのクリックイベントの話となります。
例えば、「1年前の今日」などのDatepickerのカレンダー表示と連携しない日の設定はそこまで難しくありません。

ボタンを押したら1年前の今日 を設定したい場合

$('#datepicker').datepicker({
  changeYear : true,
  changeMonth : true,
  showButtonPanel : true,
  beforeShow : function(input) {
    setTimeout(function() {
      var buttonPane = $(input).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
        	var dt = new Date();
          dt.setFullYear(dt.getFullYear() - 1);
        	$(input).datepicker("setDate", dt);
          $(input).datepicker('hide');
        }
      }).appendTo(buttonPane).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  },
  onChangeMonthYear: function(year, month, instance) {
    setTimeout(function() {
      var buttonPane = $(instance).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
          var dt = new Date();
          dt.setFullYear(dt.getFullYear() - 1);
        	$(input).datepicker("setDate", dt);
          $('#datepicker').datepicker('hide');
        }
      }).appendTo( buttonPane ).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  }
});

一方、「カレンダーで選択された年月からみて1年前の年月+今日の日付」などのカレンダー表示の値をもとに加工した日付を設定する場合はクリックイベントだけでは取得出来ません。
その場合はonCloseイベントの力を借ります。

ボタンを押したら選択されている年月日の1年前の日付を設定したい

var flag = false;
$('#datepicker').datepicker({
  changeYear : true,
  changeMonth : true,
  showButtonPanel : true,
  onClose: function(dateText, inst, dateob) {
  	if(flag){
    	var dt = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
      dt.setFullYear(dt.getFullYear() - 1);
      $('#datepicker').datepicker("setDate", dt);
    }
    flag = false;
  },
  beforeShow : function(input) {
    setTimeout(function() {
      var buttonPane = $(input).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
        	flag = true;
          $(input).datepicker('hide');
        }
      }).appendTo(buttonPane).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  },
  onChangeMonthYear: function(year, month, instance) {
    setTimeout(function() {
      var buttonPane = $(instance).datepicker('widget').find('.ui-datepicker-buttonpane');
      $('<button>', {
        text: '追加',
        click: function() {
          flag = true;
          $('#datepicker').datepicker('hide');
        }
      }).appendTo( buttonPane ).addClass('ui-datepicker-current ui-state-default ui-priority-primary ui-corner-all');
    }, 1 );
  }
});

追加したボタンのクリックイベントにはDatepickerを非表示にするのと、非表示にしたときに発火されるonCloseイベントで動くようなフラグをtrueにする処理の2つのみ。
onCloseイベントの中でinst引数からSelected○○変数から、追加したボタンがクリックされたときに選択されている年月日(画像だと2013年10月9日)を取得して、1年前の値を設定しています。

参考記事

jQuery UI datepickerの showButtonPanel にボタンを追加する
https://ledsun.hatenablog.com/entry/20120723/1343018860