SOFTELメモ Developer's blog

会社概要 ブログ 調査依頼 採用情報 ...
技術者募集中

jquery.ui.datepicker.jsに最小限の手を加えて年月選択UIを作る

日付入力、カレンダー入力をしたいときは jquery.ui.datepicker.js。

では、年月入力ができる機能が欲しいときはどうしましょう。

jquery.ui.ympicker.js デモ

今回は、jquery.ui.datepicker.jsを 改造して作る例をご紹介します。

元が jquery.ui.datepicker.js なので、オプションなどもほぼそのまま、動きもだいたい似ています。

名づけて jquery.ui.ympicker.js。

以下、調理方法です。


1、材料

jqueryuiのダウンロードページから一式ダウンロードする。

ダウンロードしたzipファイルを解凍し、中身から、開発用の元のソース(development-bundle/ui/jquery.ui.datepicker.js)を使う。


2、名前を変更する

お好みの名前にしていただいてよいですが、ここではympickerとします。

外部jsのファイル名を jquery.ui.datepicker.js → jquery.ui.ympicker.js にします。

jquery.ui.ympicker.js をエディタで開き、さらに中も変更します。置換機能のあるエディタで以下のように書き換えます。

ここまでの変更で、$(セレクタ).ympicker() で、datepickerと同じ動きをするようになります。

では、動きを変えていきます。


3、年月選択仕様に変える

どのへんを書き換えているのか分かりづらいですが、ソースの中を検索すると見つかると思います。

過程をメモしておこうと思って書いた部分なので、結果だけ欲しい場合はデモ画面をご覧ください。

カレンダーの表示を変える

カレンダーを表示している _generateHTML メソッドを書き換える。月が並ぶように変える。

・ヘッダの曜日は不要

				calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
					(/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
					(/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
					this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
					row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
//					'</div><table class="ui-datepicker-calendar"><thead>' +
					'</div><table class="ui-datepicker-calendar"><tbody>';
/*
					'<tr>';
				var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
				for (var dow = 0; dow < 7; dow++) { // days of the week
					var day = (dow + firstDay) % 7;
					thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
						'<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
				}
				calender += thead + '</tr></thead><tbody>';
*/

・カレンダーの描画のループのスタートでその年の1月1日にして、月単位でループするようにする

				//var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
				//↑いろいろ計算しているけど無視して、選択している年の1月1日にする
				var printDate = this._daylightSavingAdjust(new Date(drawYear, 0, 1));

・ 4列3行にで12ヶ月にする

//				for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
				for (var dRow = 0; dRow < 3; dRow++) { // create date picker rows
//					for (var dow = 0; dow < 7; dow++) { // create date picker days
					for (var dow = 0; dow < 4; dow++) { // create date picker days

日付を表示していたところを、月の表示に変える。

//							'" href="#">' + printDate.getDate() + '')) + ''; // display selectable date
							'" href="#">' + monthNamesShort[printDate.getMonth()] + '')) + ''; // display selectable date

1日ずつ足してループしているところを、1ヶ月ずつ足してループするようにする。

//						//printDate.setDate(printDate.getDate() + 1);
						printDate.setMonth(printDate.getMonth() + 1);

日付選択時の動きを変える

対象:_selectDay メソッド

datepickerではカレンダーの各セルのHTMLから日付を取得するようになっています。年月入力化するとセルには月の名前を入れることになるので、そこから日付は取れません。日付は引数で渡すように変えます。

	/* Action for selecting a day. */
//	_selectDay: function(id, month, year, td) {
	_selectDay: function(id, month, year, d, td) {
		var target = $(id);
		if ($(td).hasClass(this._unselectableClass) || this._isDisabledYmpicker(target[0])) {
			return;
		}
		var inst = this._getInst(target[0]);
//		inst.selectedDay = inst.currentDay = $('a', td).html();
		inst.selectedDay = inst.currentDay = d;
		inst.selectedMonth = inst.currentMonth = month;
		inst.selectedYear = inst.currentYear = year;
		this._selectDate(id, this._formatDate(inst,
			inst.currentDay, inst.currentMonth, inst.currentYear));
	},

_selectDay メソッドを使っている箇所が2箇所あるので、そちらも変えます。

//530行目付近
				case 13: var sel = $('td.' + $.ympicker._dayOverClass + ':not(.' + 
									$.ympicker._currentClass + ')', inst.dpDiv);
						if (sel[0])
//							$.ympicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
							$.ympicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, 1, sel[0]);
//1550行目付近
						tbody += '<td class="' +
//(中略)
							((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
							(unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.ympicker._selectDay(\'#' +
//							inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
							inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ',' + printDate.getDate() + ', this);return false;"') + '>' +
							(otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
							(unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
//(後略)

カレンダーのヘッダ部分の変更

年月が表示されていたところを、年のみにするよう _generateMonthYearHeader を書き換える。

月を表示しているところを全部コメントアウト。

		// month selection
/*
		if (secondary || !changeMonth)
			monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
		else {
			var inMinYear = (minDate && minDate.getFullYear() == drawYear);
			var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
			monthHtml += '<select class="ui-datepicker-month" ' +
				'onchange="DP_jQuery_' + dpuuid + '.ympicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
			 	'>';
			for (var month = 0; month < 12; month++) {
				if ((!inMinYear || month >= minDate.getMonth()) &&
						(!inMaxYear || month <= maxDate.getMonth()))
					monthHtml += '<option value="' + month + '"' +
						(month == drawMonth ? ' selected="selected"' : '') +
						'>' + monthNamesShort[month] + '</option>';
			}
			monthHtml += '</select>';
		}
		if (!showMonthAfterYear)
			html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
*/

4、デフォルトの設定値を変える

指定がなければ以下の動作をするように、設定のデフォルト値を変えます。

this._defaultsのところで、

・年はプルダウン表示する → changeYear: true
・他の月も表示する → showOtherMonths: true
・他の月も選択可能にする → selectOtherMonths: true
・12ヶ月単位で移動すること → stepMonths: 12
・表示形式を年月に → dateFormat: ‘mm/yy’

5、使う

デフォルトのオプションも変えてしまったので、datepickerと同じように使えるでしょう。

//そのまま
$(セレクタ).ympicker();
//日本語仕様オプション
$(セレクタ).ympicker({
    closeText: '閉じる',
    prevText: '<前',
    nextText: '次>',
    currentText: '今日',
    monthNames: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
    monthNamesShort: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
    dateFormat: 'yy/mm',
    yearSuffix: '年'
});

demo 年月入力 jquery.ui.ympicker.js デモ

DOWNLOAD jquery.ui.ympicker – ダウンロード


@todo

対応していない部分は結構あって、キーボードでもある程度操作できるところなどまではカスタマイズしていません。


以上、若干忘れ物があるかもしれませんので、お気づきの点などありましたら教えてください。

よろしくお願いします。

関連するメモ

コメント(7)

ららら 2013年7月18日 10:59

カレンダー表示時、選択値をデフォルトでセットしてくれなかったので、
以下の修正を追加しました。
_setDateFromField
inst.drawMonth = ~~inst.lastVal.substring(5,7)-1;
inst.drawYear = ~~inst.lastVal.substring(0,4);
//inst.drawMonth = inst.selectedMonth = date.getMonth();
//inst.drawYear = inst.selectedYear = date.getFullYear();
無理やりなので、何か他に良い方法をご存じでしたら、ご教示ください。

naga 2014年10月17日 15:07

通りがかりのものです。
ちょうどいいと思い、利用させていただきましたが、
jquery の最新版との利用ができないようです。ネットで検索してみると、
jquery1.8.3 から 1.9.0 へアップされたときに、APIが変わったとのことで、

これを、併記させておけば、利用できました。
詳しくは、
http://myjquery.blog.fc2.com/blog-entry-15.html
このソフトがなくても、いいように、改修できればいいのですが、わたしには、時間がかかりそうです。
もしよければ、貴方様の手で、改修していただければ幸いです。
よろしくお願いいたします。

KAme 2015年1月22日 11:13

1582行目を以下に変えればJquery1.9以降でも動きますね。
html += buttonPanel + (!$.support.noCloneChecked && !inst.inline ?

binhaer 2015年8月18日 16:50

GOOGLE chrome safari 上で動き 問題ないですが、
IE上で 問題ありそうです
カレンダーの下で 長い白枠があって そしてfalse 文字が乗せてます
何か どこ 問題がありますか?

onoki 2015年9月16日 20:08

maxDate, minDateを設定した際に、範囲外の月名が短縮月名ではなく「1」となるバグがありました
1559行目 printDate.getDate() → monthNamesShort[printDate.getMonth()] と修正すればOKです
周辺ソースは対応されていましたので、修正漏れかと思います

miura 2020年6月22日 13:13

一通り探してみた中で一番ユーザビリティーが良さそうだったので使わせていただきました。
Jquery.uiで実装されている「onSelect」の様な月の選択後関数を追加できるようなオプションは可能でしょうか?

めんこ仮面 2022年7月8日 19:43

minDateに未来日付を指定すると選択値(.ui-state-active)の表示がminDateの月固定になってしまったので、処理を追ってみました。
parseDate関数でテキストボックスに入力されている値を解析する際に「日」の部分が取得できないため(年月しか入力されませんしね)、
関数の最後のところでやってるエラー判定の「date.getDate() != day」に引っ掛かって「Invalid date」をスローしていました。
「日」が取れないなら固定にしてあげればいいじゃないかということで、
1001行目を
// var day = -1;
var day = 1;
という様に1日が入力されてる事にしたらうまく行きました。