【jQuery】カレンダーの作り方

#【jQueryでカレンダーを作ってみた】
jQueryの勉強がてらに、カレンダーを作成したので、要点メモ

###今回作成する主なカレンダーの機能
・年、月、日、曜日の出力
・前月、次月の切り替え
・表示、非表示の切り替え

以上のようなカレンダーを作っていきます。


###用意したHTMLはコチラ
```html:index.html
<!doctype html>
<html lang="ja">
<head>
<meta name="robots" content="noindex"><!-- クローラー回避 -->
<meta charset="utf-8">
<title>カレンダー</title>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="js/schedule.js" type="text/javascript"></script><!-- ここで読み込まなくても可 -->
<script src="js/calendar.js" type="text/javascript"></script>
<!-- css -->
<link href="css/normalize.css" rel="stylesheet" type="text/css">
<link href="css/style.css" rel="stylesheet" type="text/css">
</head>
<body>
<form method="post" action="#">
<input type="text" value="日付を入力してください" id="calendar_form">
<button type="submit">送信</button>
</form>
<div id="calendar"></div>
</body>
</html>
```
jQueryのバージョンは3.4.1


###■ schedule.js
まず、休日設定用のデータを作成

```javascript:schedule.js
// 休日指定したい日付を連想配列で指定しておく
var holidays = {
	// 休日設定していない場合のために、デフォルト値を設定
	'default':[
		{'1':[]},
		{'2':[]},
		{'3':[]},
		{'4':[]},
		{'5':[]},
		{'6':[]},
		{'7':[]},
		{'8':[]},
		{'9':[]},
		{'10':[]},
		{'11':[]},
		{'12':[]}
	],// 設定例：2019年1月1,2,3日が休日の場合　'2019':[{1:[1,2,3]}]
	'2019':[
		{'1':[]},
		// 省略
		{'12':[]}
	]
}
```
もっとスマートに描く方法はないものか、、、
連想配列の“キー”に“年”、“月”を設定し、該当の“日”を割り出せるように作成


###■ calendar.js
カレンダー本体の作成

・カレンダー本体を出力するボタン作成
・操作ボタンを出力
・日付クリックで日付情報取得

```javascript:calendar.js
/*global $*/
/* jQuery Contents */
$(function(){
	// カレンダー出力ボタンの設置
	distBtn.init();
	// カレンダーの表示非表示の制御
	var clickCount = 0;
	dBtn.on('click', function(){
		if(closeBtn != 'off'){
			clickCount++;
		}
		if(closeBtn == 'off'){
			clickCount = clickCount + 0;
		}
		if(clickCount%2 == 1){
			calendar.init();
			calendarPanel.init();
			clickToActions.init();
			dataDist.init();
			closeCalBtn.init();
			$('#calendar').css('display', 'none').fadeIn();
		}else{
			$("#btns").fadeOut(function(){
				$(this).remove();
			});
			calInner.fadeOut(function(){
				$(this).remove()
			});
		}
	});
});

// calendar close function
var closeCalBtn = (function(){
	function init(){
		closeBtn.on('click', function(){
			$("#btns").fadeOut(function(){
				$(this).remove();
			});
			calInner.fadeOut(function(){
				$(this).remove()
			});
			closeBtn = 'off';
		});
	}
	return {init:init}
})();

var calInner = '';
var closeBtn = '';
//////////////////////////// calendar close


// calendar dist btn function
var distBtn = (function(){
	function init(){
		var distBtn = '<div id="calBtn"><span>calendar</span></div>'
		$("#calendar").before(distBtn);
		
		dBtn = $("#calBtn");
	}
	return {init:init}
})();

var dBtn = '';
//////////////////////////// calendar dist

// 祝日設定 html上でなく、ここでも取得可
//$.get('js/schedule.js');

/*-----------------------
	variables
------------------------*/
var now, now_m, now_y;
now = new Date();
now_m = now.getMonth()+1;
now_y = now.getFullYear();
var weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
var prevText = 'prev';
var nextText = 'next';

var yearData = '';
var monthData = '';
var weekData = '';
var daysArea = '';
var prevdaysArea = '';
var nextdaysArea = '';

/*-----------------------
	clickToActions
------------------------*/
var clickToActions = (function(){
	function init() {
		var _cal = '';
		var dt = now;
		$("#btns a").on("click", function(){
			if($(this).is(":contains("+prevText+")")){
				_cal = $("#calInner");
				_cal.remove();
				dt.setDate(1);
				dt.setMonth(dt.getMonth()-1);
				now_m = dt.getMonth()+1;
				now_y = dt.getFullYear();
				calendar.init();
				dataDist.init();
				closeCalBtn.init();
			}
			if($(this).is(":contains("+nextText+")")){
				_cal = $("#calInner");
				_cal.remove();
				dt.setDate(1);
				dt.setMonth(dt.getMonth()+1);
				now_m = dt.getMonth()+1;
				now_y = dt.getFullYear();
				calendar.init();
				dataDist.init();
				closeCalBtn.init();
			}
		});
	}
    return {init:init};
})();

/*-----------------------
	calendar panel
------------------------*/
var calendarPanel = (function(){
	function init() {
		// 操作パネル追加
		var panelHtml = '<div id="btns"><a>'+prevText+'</a><a>'+nextText+'</a></div>';
		$("#calendar").append(panelHtml);
	}
	return {init:init};
})();

/*-----------------------
	calendar
------------------------*/
var calendar = (function(){
	function init() {
		// 月の末日出力
		function getMonthDays(year, month) {
			return new Date(year, month, 0).getDate();
		}
		// 引数が当月なら本日の日を返す
		var isToday = (function istoday(year, month){
			var _ = new Date();
			var toYear = _.getFullYear();
			var toMonth = _.getMonth()+1;
			if(toMonth == month && toYear == year){
				return new Date().getDate();
			}else{
				return false;
			}
		});
		// 当月日数分表示(祝日にはclass="holiday")
		var gmd = getMonthDays(now_y, now_m);
		var check = '';
		if(holidays[now_y]){
			check = holidays[now_y][now_m-1];
		}else{
			check = holidays['default'][now_m-1];
		}
		var daysHTML = '';
		for(var i=1; i<=gmd; ++i){
			if(check[now_m].indexOf(i) >= 0){
				daysHTML += '<li class="holiday"><span>' + i +'</span></li>';
			}else if(isToday(now_y, now_m) == i){
				daysHTML += '<li class="today"><span>' + i +'</span></li>';
			}else{
				daysHTML += '<li><span>' + i +'</span></li>';
			}
		}
		// 前月端数表示
		var monthFirstWeekNum = new Date(now_y, now_m - 1, 1).getDay();
		var beforeDt = new Date(now_y,now_m - 1, 0);

		var prevDays = beforeDt.getDate()+1;
		var prevHTML = '';
		for(var num=0; num<monthFirstWeekNum; ++num){
			prevDays = prevDays -1;
			prevHTML = '<li class="prev"><span>'+prevDays+'</span></li>'+prevHTML;
		}
		// 翌月端数表示
		var monthLastWeekNum = new Date(now_y, now_m, 0).getDay();
		var nextDays = 0;
		var nextHTML = '';
		for(var numb=monthLastWeekNum+1; numb<=6; ++numb){
			nextDays++;
			nextHTML += '<li class="next"><span>'+nextDays+'</span></li>';
		}
		
		var html = '';
		
		html = '<div id="calInner">';
		html += '<div id="closeBtn"><a>close</a></div>';
		html += '<div id="year"><p>'+now_y+'</p></div>';
		html += '<div id="month"><p>'+now_m+'</p></div>';
		
		var week = '';
		$.each(weekdays, function(index, value){
			week += '<li><span>'+value+'</span></li>';
		});
		
		html += '<div id="week"><ul>'+week+'</ul></div>';
		
		html += '<ul>' + prevHTML + daysHTML + nextHTML + '</ul></div>';
		
		// id="calendar"取得
		var cal = $('#calendar');
		
		// html出力
		cal.prepend(html);
		
		calInner = $("#calInner");
		closeBtn = $("#closeBtn");
		yearData = $("#year");
		monthData = $("#month");
		daysArea = $("#calInner>ul li:not(.prev, .next)");
		prevdaysArea = $("#calInner>ul li.prev");
		nextdaysArea = $("#calInner>ul li.next");
		weekData = $("#week>ul li");
    }
    return {init:init};
})();

// クリックで文字列取得
var dataDist = (function(){
	function init(){
		daysArea.on('click', function(){
			daysArea.removeClass("on");
			prevdaysArea.removeClass("on");
			nextdaysArea.removeClass("on");
			$(this).addClass("on");
			var otherDt = new Date(yearData.text(), monthData.text() - 1, $(this).text()).getDay();
			var otherWeek = weekdays[otherDt];
			var dateData = yearData.text() + "年" + monthData.text() + "月" + $(this).text() + "日" + "(" + otherWeek + ")";
			$('#calendar_form').attr('value', dateData);
		})
		prevdaysArea.on('click', function(){
			daysArea.removeClass("on");
			prevdaysArea.removeClass("on");
			nextdaysArea.removeClass("on");
			$(this).addClass("on");
			var opDt = new Date(yearData.text(), monthData.text() - 2, $(this).text()).getDay();
			var opWeek = weekdays[opDt];
			var pMonth = monthData.text() - 1;
			var pdateData = yearData.text() + "年" + pMonth + "月" + $(this).text() + "日" + "(" + opWeek + ")";
			$('#calendar_form').attr('value', pdateData);
		})
		nextdaysArea.on('click', function(){
			daysArea.removeClass("on");
			prevdaysArea.removeClass("on");
			nextdaysArea.removeClass("on");
			$(this).addClass("on");
			var onDt = new Date(yearData.text(), monthData.text(), $(this).text()).getDay();
			var onWeek = weekdays[onDt];
			var nMonth = parseInt(monthData.text()) + parseInt(1);
			var ndateData = yearData.text() + "年" + nMonth + "月" + $(this).text() + "日" + "(" + onWeek + ")";
			$('#calendar_form').attr('value', ndateData);
		})
		yearData.on('click', function(){
			yearData.removeClass("on");
			$(this).addClass("on");
		})
		monthData.on('click', function(){
			monthData.removeClass("on");
			$(this).addClass("on");
		})
		weekData.on('click', function(){
			weekData.removeClass("on");
			$(this).addClass("on");
		})
	}
	return {init:init}
})();
```
これでhtmlの出力制御を行い、見た目をcssで整形

用途不明だが、カレンダーでフォームの日付入力するのに役立つかも…