jiichan.com

PROGRAMMING

javascript
CSS
PHP
Java
SQL

自作カレンダーをES6で使えるようになったクラスで置き替えてみる

JavaScriptの標準規格であるES2015(ES6)で、クラスが使えるようになりました。 そこで以前作った自作カレンダーをクラスに置き替えてみました。
変数も関数スコープのvarから、新しい仕様のletとconstに替えました。

まずはクラスの宣言とコンストラクター

今までの関数カレンダーは単独の外部ファイルになっていて、 そのjsファイルを別のjsファイルに読み込んで 使っていました。
なので、クラス化したファイルも当然すんなり読み込まれると思っていましたが、どういう訳か読み込み出来ませんでした。
ネットを見ていると「ブラウザで覚えるES Modules入門 JavaScriptでモジュールを使う時代」というのがあり、その通りやってみると上手くいきました。
そこで、classの前に、モジュールとして使えるようにexportを付与してあります。
exportのついたものが別jsファイルでimportできます。


export class MkenCalendarClass {
	constructor(tagId) {
		const today = new Date();
		// 今日の年月日
		this.todayYear = today.getFullYear();
		this.todayMonth = today.getMonth() + 1;
		this.todayDate = today.getDate();

		//	プロパティnenn.tukiは次月・前月クリックで変化する値
		// カレンダーの年
		this.nenn = this.todayYear;
		// カレンダーの月
		this.tuki = this.todayMonth;
		// カレンダーの日
		this.nichi = this.todayDate;

		// 年月日の区切り文字
		this.spliter = "_";
		// 現在選択されている日付
		this.currentDay = 
			this.nenn + this.spliter 
				+ this.tuki + this.spliter + this.nichi;

		// 各月の日数
		this.monthDays = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
		// 表示されている月の前月の年
		this.prevNenn;
		// 表示されている月の次月の年
		this.nextNenn;
		// 表示されている月の前月
		this.prevTuki;
		// 表示されている月の次月
		this.nextTuki;

		this.tagId = tagId;
	}

また、プロパティ(フィールド?)をクラス宣言直下に置いたらエラーが出て、 プロパティはcostructor内に置かれなければいけないことも分かりました。

各メソッド(前月・次月指定時の処理)

カレンダー前月や次月をクリックした時の年月を得るためのメソッドを追加します。


	setNengetsu(dore) {
		// 次月
		if(dore === 1){
			// ひと月プラスする
			this.tuki++;
		}
		// 前月
		if(dore === -1){
			// ひと月マイナスする
			 this.tuki--;
		}
		// 結果(月)が13ならば
		if(this.tuki === 13){
			// 年を一年プラスして
			this.nenn++;
			// 月を一月に
			this.tuki = 1;
		}
		// 結果(月)が0になったら
		if(this.tuki === 0){
			// 年を一年マイナスして
			this.nenn--;
			// 月を12月に
			this.tuki = 12;
		}
		// 閏年の場合
		if(this.isLeapYear(this.nenn))
			// 2月を29日に
			this.monthDays[1] = 29;
		else
			// その他は28日に
			this.monthDays[1] = 28;
	}

各メソッド(カレンダーの前月・次月部分の日付の年月を算出)

カレンダーの前月・次月部分の日付の年月を算出用のメソッドです。


	setPrevNext() {
		if(this.tuki === 1){
			this.prevNenn = this.nenn - 1;
			this.prevTuki = 12;
		}else{
			this.prevNenn = this.nenn;
			this.prevTuki = this.tuki - 1;
		}
		if(this.tuki === 12){
			this.nextNenn = this.nenn + 1;
			this.nextTuki = 1;
		}else{
			this.nextNenn = this.nenn;
			this.nextTuki = this.tuki + 1;
		}
	}

各メソッド(カレンダーのマスのみ作成)

カレンダーのマスとclass.idのみをセットし、変化する年月や日付は別にセットするようにしました。
週の数は、カレンダー表示の際に日数によって上下のサイズが変わるのは嫌なので6週固定にしました。


	createHTML() {
		// 年月の選択行を生成 =======================================
		const rowNengetsu = document.createElement("tr");	
		rowNengetsu.id = this.tagId + "NengetsuID";

		// 月の表示セル
		const cellMm = document.createElement("td");
		cellMm.id = this.tagId + "MmID";
		cellMm.appendChild(document.createTextNode(""));
		rowNengetsu.appendChild(cellMm);

		// 年の表示セル
		const cellYy = document.createElement("td");
		cellYy.colSpan = "4";
		cellYy.id = this.tagId + "YyID";
		cellYy.appendChild(document.createTextNode(""));
		rowNengetsu.appendChild(cellYy);

		// 前へセル
		const cellPrev = document.createElement("td");
		cellPrev.id = this.tagId + "PrevID";
		cellPrev.appendChild(document.createTextNode("←"));
		rowNengetsu.appendChild(cellPrev);

		// 次へセル
		const cellNext = document.createElement("td");
		cellNext.id = this.tagId + "NextID";
		cellNext.appendChild(document.createTextNode("→"));
		rowNengetsu.appendChild(cellNext);

		// 曜日の行を生成 =========================================
		const rowWeeks = document.createElement("tr");
		rowWeeks.id = this.tagId + "WeeksID";

		// 曜日のセル
		for(let i = 0; i <= 6; i++){
			const cellWeek = document.createElement("td");
			switch (i) {
				case 0:
					cellWeek.appendChild(document.createTextNode("SUN"));
						break;
				case 1:
					cellWeek.appendChild(document.createTextNode("MON"));
						break;
				case 2:
					cellWeek.appendChild(document.createTextNode("TUE"));
						break;
				case 3:
					cellWeek.appendChild(document.createTextNode("WEN"));
						break;
				case 4:
					cellWeek.appendChild(document.createTextNode("THU"));
						break;
				case 5:
					cellWeek.appendChild(document.createTextNode("FRI"));
						break;
				case 6:
					cellWeek.appendChild(document.createTextNode("SAT"));
						break;
				default:
			}
			rowWeeks.appendChild(cellWeek);
		}

		const tblBody = document.createElement("tbody");
		tblBody.appendChild(rowNengetsu);
		tblBody.appendChild(rowWeeks);

		// 日付部
		// 一週間を6週(行)繰り返し
		for(let j = 0; j < 6; j++){
			const row = document.createElement("tr");
			// 1日を7日(列)繰り返し
			for(let k = 0; k <= 6; k++){
				const cell = document.createElement("td");
				const cellText = document.createTextNode("");
				cell.className = this.tagId + "DayCLS";
				cell.appendChild(cellText);
				row.appendChild(cell);
			}
			tblBody.appendChild(row);
		}

		const tbl = document.createElement("table");
		tbl.id = this.tagId + "TableID";
		tbl.appendChild(tblBody);

		document.getElementById(this.tagId).appendChild(tbl);

	}

class名やid名は、頭にtagIdをプラスして他のコードと重複しないようにしました。

各メソッド(各マスへ年月・日付を埋め込み)

この部分が今回の修正のメインともいえる部分でした。
表示される当月のついたちの曜日(0:日曜~6:土曜)を得、1からついたちの曜日をマイナスして前月部分を得ました。
翌月部分はhizukeのカウンタから当月の日数をマイナスして算出しています。
classListは本当に便利だと思いました。


	setDays() {
		// 月を埋め込み
		const Mm = document.getElementById(this.tagId + "MmID");
		const txtMm = document.createTextNode(this.tuki);
		Mm.replaceChild(txtMm, Mm.firstChild);
		// 年を埋め込み
		const Yy = document.getElementById(this.tagId + "YyID");
		const txtYy = document.createTextNode(this.nenn);
		Yy.replaceChild(txtYy, Yy.firstChild);

		// 表示される月のついたちの曜日(0:日曜~6:土曜)
		const yobidate = new Date(this.nenn, this.tuki - 1, 1);
		const tuitati_yobi = yobidate.getDay(); 
		// 最初のマスの日付(その月の1日以前はマイナス何日となる)
		let hizuke = 1 - tuitati_yobi;

		// 前月・次月部分の年と月の値をセット
		this.setPrevNext();					

		// 閏年の場合
		if(this.isLeapYear(this.nenn))
			this.monthDays[1] = 29;
		else
			this.monthDays[1] = 28;

		let txtHizuke;
		// 日付マスの要素取得
		const elements 
				= document.getElementsByClassName(this.tagId + "DayCLS");
		for (let i = 0; i < elements.length; i++) {
			// 既にセット済みのクラスを初期化(削除)
			elements[i].classList.remove(this.tagId + "PrevMonthCLS");
			elements[i].classList.remove(this.tagId + "CurrentMonthCLS");
			elements[i].classList.remove(this.tagId + "NextMonthCLS");

			// 前月部分
			if(hizuke <= 0){
				elements[i].classList.add(this.tagId + "PrevMonthCLS");
				txtHizuke =
					document.createTextNode(
						this.monthDays[this.prevTuki-1]+hizuke);
			}
			// 当月部分
			if((hizuke >= 1) && (hizuke <= this.monthDays[this.tuki-1])) {
				elements[i].classList.add(this.tagId + "CurrentMonthCLS");
				txtHizuke = document.createTextNode(hizuke);
			}
			// 次月部分
			if((hizuke > this.monthDays[this.tuki - 1])) {
				elements[i].classList.add(this.tagId + "NextMonthCLS");
				txtHizuke =
					document.createTextNode(
						hizuke - this.monthDays[this.tuki-1]);
			}
			elements[i].replaceChild(txtHizuke, elements[i].firstChild);
			hizuke++;
		}
	}

各メソッド(スタイルの設定)

祝日や透明度など、前月や翌月そして当月ごとにコードを書かなければならないので行数が多くなってしまいました。


	setStyle() {
		// テーブル全体のスタイル
		// border paddingを含んだwidthに
		const boxSizingTBL = "border-box";
		// 境界線を重ねて表示
		const borderCollapseTBL = "collapse";
		// カレンダー全体の背景色
		const backgroundColorTBL = "#edeaea";
		// 全体のフォント
		const fontFamilyTBL = "Meiryo";
		// フォントの太さ
		const fontWeightTBL = "600";
		// 全体の文字表示位置(水平方向)
		const textAlignTBL = "center";
		// 全体の文字表示位置(垂直方向)
		const verticalAlignTBL = "middle";

		// 日付セルのスタイル
		// 日付のマス幅 border-box用 スマホ用
		const widthCL = "42px";
		// 日付のマス高さ border-box用 スマホ用
		const heightCL = "28px";
		// 日付のマス内余白
		const paddingCL = "3px 9px 0px 0px";
		// 日付文字サイズ スマホ用
		const fontSizeCL = "11px";
		// 日付の文字表示位置(水平方向)
		const textAlignCL = "right";
		// 日付欄のカーソル形状
		const cursorCL = "pointer";

		// マウスオーバー時のフォントサイズ
		const fontSizeOVER = "12px";

		// 今日の場合の背景色
		const backgroundColorTODAY = "#ffffff";
		// クリックした日付の背景色、
		const clickDayBackGroundColor = "#ffff99";

		// 曜日の文字色と背景色
		// 日曜の文字色(当月)
		const colorSUN = "rgba(255,0,0,1.0)";
		// 平日の文字色(当月)
		const colorHEI = "rgba(85,85,85,1.0)";
		// 土曜の文字色(当月)
		const colorSAT = "rgba(0,153,255,1.0)";
		// 日曜の文字色(当月以外)
		const colorSUN2 = "rgba(255,0,0,0.2)";
		// 平日の文字色(当月以外)
		const colorHEI2 = "rgba(85,85,85,0.2)";
		// 土曜の文字色(当月以外)
		const colorSAT2 = "rgba(0,153,255,0.2)";
		// 日曜の背景色
		const backgroundColorSUN = "#dddddd";
		// 土曜の背景色
		const backgroundColorSAT = "#dddddd";

		// 年月部のスタイル
		// 年月等の背景色
		const backgroundColorYM = "#f9f4f4";
		// 年月等の文字色
		const colorYM = "#555555";
		// 月の文字サイズ
		const fontSizeMM = "23px";
		// 年の文字サイズ
		const fontSizeYY = "11px";
		// 矢印の文字サイズ
		const fontSizeAL = "15px";
		// 年月等の高さ
		const heightYM = "30px";
		// 年の文字位置(水平方向)
		const textAlignYY = "left";
		// 年の文字表示位置(垂直方向)
		const verticalAlignYY = "bottom";
		// 月のマス内余白
		const paddingMM = "5px 0px 0px 0px";

		// テーブル全体
		const TableTag = 
			document.getElementById(this.tagId + "TableID");
		TableTag.style.boxSizing = boxSizingTBL;
		TableTag.style.borderCollapse = borderCollapseTBL;
		TableTag.style.backgroundColor = backgroundColorTBL;
		TableTag.style.fontFamily = fontFamilyTBL;
		TableTag.style.fontWeight = fontWeightTBL;
		TableTag.style.textAlign = textAlignTBL;
		TableTag.style.verticalAlign = verticalAlignTBL;

		// 年月部
		TableTag.rows[0].style.backgroundColor = backgroundColorYM;
		TableTag.rows[0].style.color = colorYM;
		TableTag.rows[0].style.height = heightYM;
		TableTag.rows[0].cells[0].style.fontSize = fontSizeMM;
		TableTag.rows[0].cells[0].style.padding = paddingMM;
		TableTag.rows[0].cells[1].style.fontSize = fontSizeYY;
		TableTag.rows[0].cells[1].style.textAlign = textAlignYY;
		TableTag.rows[0].cells[1].style.verticalAlign = verticalAlignYY;
		TableTag.rows[0].cells[2].style.fontSize = fontSizeAL;
		TableTag.rows[0].cells[2].style.cursor = cursorCL;
		TableTag.rows[0].cells[3].style.fontSize = fontSizeAL;
		TableTag.rows[0].cells[3].style.cursor = cursorCL;

		// 曜日部
		TableTag.rows[1].style.backgroundColor = backgroundColorYM;
		for(let i = 0; i < TableTag.rows[1].cells.length; i++){
			TableTag.rows[1].cells[i].style.width = widthCL;
			TableTag.rows[1].cells[i].style.height = heightCL;
			TableTag.rows[1].cells[i].style.fontSize = fontSizeCL;
			if(i === 0){TableTag.rows[1].cells[i].style.color = colorSUN;}
			if((i >= 1) && (i <= 5)){
				TableTag.rows[1].cells[i].style.color = colorHEI;
			}
			if(i === 6){TableTag.rows[1].cells[i].style.color = colorSAT;}
		}

		// 日付セル部
		for(let j = 2; j < TableTag.rows.length; j++){
			const row = TableTag.rows[j];
			for(let i = 0; i < row.cells.length; i++){
				const cell = row.cells[i];
				cell.style.width = widthCL;
				cell.style.height = heightCL;
				cell.style.padding = paddingCL;
				cell.style.textAlign = textAlignCL;
				cell.style.fontSize = fontSizeCL;
				cell.style.cursor = cursorCL;
				// 各曜日の文字色
				if(i === 0){cell.style.color = colorSUN;}
				if((i >= 1) && (i <= 5)){cell.style.color = colorHEI;}
				if(i === 6){cell.style.color = colorSAT;}
				// 各曜日の背景色
				let checkDay =
					this.nenn + this.spliter + this.tuki 
						+ this.spliter+ cell.firstChild.nodeValue;
				let toDay =
					this.todayYear + this.spliter + this.todayMonth
					+ this.spliter + this.todayDate;
				if((checkDay === toDay) 
				&& (cell.classList.contains(this.tagId + "CurrentMonthCLS")))
				{
					// 今日の場合
					cell.style.backgroundColor = backgroundColorTODAY;
				}else{
					if(i === 0){
						cell.style.backgroundColor = backgroundColorSUN;}
					if((i >= 1) && (i <= 5)){
						cell.style.backgroundColor = backgroundColorTBL;}
					if(i === 6){
						cell.style.backgroundColor = backgroundColorSAT;}
				}
				// 日付部分のマウスオーバー・マウスアウト(文字のサイズとカーソル形状)
				cell.onmouseover =
					function(){this.style.fontSize = fontSizeOVER;};
				cell.onmouseout =
					function(){this.style.fontSize = fontSizeCL;};

				// 前月部の場合
				if(cell.classList.contains(this.tagId + "PrevMonthCLS")){
					// 文字の透明度
					if(i === 0){cell.style.color = colorSUN2;}
					if((i >= 1) && (i <= 5)){
						cell.style.color = colorHEI2;}
					if(i === 6){cell.style.color = colorSAT2;}
					// 祝日の場合
					const syukuNo =this.holiday(
					 this.prevNenn,this.prevTuki,cell.firstChild.nodeValue);
					if(syukuNo >= 0){
						cell.style.color = colorSUN2;
					}
					// 現在選択されている日付の場合は背景色を変更
					checkDay =
						this.prevNenn + this.spliter + this.prevTuki
						+ this.spliter + cell.firstChild.nodeValue;
					if(checkDay === this.currentDay){
						cell.style.backgroundColor = clickDayBackGroundColor;
					}
				}
				// 当月部の場合
				if(cell.classList.contains(this.tagId + "CurrentMonthCLS")){
					// 透明度
					cell.style.opacity = "1";
					// 祝日の場合
					const syukuNo =
					 this.holiday(
					  this.nenn, this.tuki, cell.firstChild.nodeValue);
					if(syukuNo >= 0){
					 cell.style.color = colorSUN;
					}
					// 現在選択されている日付の場合は背景色を変更
					checkDay =
					  this.nenn + this.spliter + this.tuki
					   + this.spliter + cell.firstChild.nodeValue;
					if(checkDay === this.currentDay){
					  cell.style.backgroundColor = clickDayBackGroundColor;
					}
				}
				// 次月部の場合
				if(cell.classList.contains(this.tagId + "NextMonthCLS")){
				  // 文字の透明度
				  if(i === 0){cell.style.color = colorSUN2;}
				  if((i >= 1) && (i <= 5)){cell.style.color = colorHEI2;}
				  if(i === 6){cell.style.color = colorSAT2;}
					// 祝日の場合
					const syukuNo =
					 this.holiday(
					  this.nextNenn, this.nextTuki, cell.firstChild.nodeValue);
					if(syukuNo >= 0){
						cell.style.color = colorSUN2;
					}
					// 現在選択されている日付の場合は背景色を変更
					checkDay =
					 this.nextNenn + this.spliter + this.nextTuki
					  + this.spliter + cell.firstChild.nodeValue;
					if(checkDay === this.currentDay){
					 cell.style.backgroundColor = clickDayBackGroundColor;
					}
				}
			}
		}
	}

各メソッド(イベントの設定)

イベントは「前月クリック」と「次月クリック」それに「日付クリック」です。
クリックされた日付はプロパティclickDayに入ります。
ここも結構なコード量になってしまいました。


	setEvent() {
		const self = this;	

		// 前月をクリック
		self.addListener(document.getElementById(self.tagId + 'PrevID'),
			'click', function(){
				self.setNengetsu(-1);
				self.setDays();
				self.setStyle();
		});

		// 次月をクリック
		self.addListener(document.getElementById(self.tagId + 'NextID'),
			'click', function(){
				self.setNengetsu(1);
				self.setDays();
				self.setStyle();
		});

		// 日付をクリック
		const elements =
      document.getElementsByClassName(self.tagId + "DayCLS");
		for (let i = 0; i < elements.length; i++) {
			this.addListener(elements[i], 'click', 
				function(){
					// クリックした日付をプロパティに格納
					const dd = this.firstChild.nodeValue;
					// 前月部分クリック
					// classListにPrevMonthCLSがあれば
					if(this.classList.contains(self.tagId + "PrevMonthCLS")) {
					  self.currentDay =
						  self.prevNenn + self.spliter + self.prevTuki 
						   + self.spliter + dd;
					  self.removeDate(self.prevNenn, self.prevTuki, dd);
					}
					// 当月部分クリック
					if(this.classList.contains(self.tagId + "CurrentMonthCLS")) {
					  self.currentDay =
						  self.nenn + self.spliter + self.tuki 
						   + self.spliter + dd;
					}
					// 次月部分クリック
					if(this.classList.contains(self.tagId + "NextMonthCLS")) {
					  self.currentDay =
						  self.nextNenn + self.spliter + self.nextTuki 
						   + self.spliter + dd;
					  self.removeDate(self.nextNenn, self.nextTuki, dd);
					}

					self.setStyle();
				}
			);
		}
	}

上記のイベント登録用関数。


	addListener(elm, ev, listener) {
		// IE以外
		if(elm.addEventListener) {
			elm.addEventListener(ev, listener, false);
		// IE
		} else if(elm.attachEvent) {
			elm.attachEvent('on' + ev, listener);
		} else {
			throw new Error('イベントリスナーに未対応です。');
		}
	}

各メソッド(祝休日の判定)

祝休日のメソッドは以下のとおりで、戻り値として配列のインデックスを返すようにしました。
祝休日でない場合の戻り値は-1となります。


	holiday(toshi, tsuki, hi){
		const syukujitsu = [];
		let syuku_cnt = 0;
		let nichi;
		let syukuDay;

		/* 国民の祝日(決められた日 16日あり) */
		// 元日(1月1日)
		syukujitsu[0] = 
			toshi + this.spliter + "1" + this.spliter + "1";
		// 成人の日(1月の2月曜日)
		nichi = this.howDay(toshi, 1, 2, 1);
		syukujitsu[1] = 
			toshi + this.spliter + "1" + this.spliter + nichi;
		// 建国記念日(2月11日)
		syukujitsu[2] = 
			toshi + this.spliter + "2" + this.spliter + "11";
		// 新天皇誕生日(2月23日) ************************************
		if(toshi >= 2020) {
			syukujitsu[3] = 
				toshi + this.spliter + "2" + this.spliter + "23";
		}
		// 春分の日(前年の2月1日官報に掲載)・簡易計算法
		const haru =
			Math.floor(
				20.8431+0.242194*(toshi-1980)-Math.floor((toshi-1980)/4));
		syukujitsu[4] = toshi + this.spliter + "3" + this.spliter + haru;
		// 昭和の日(4月29日)
		syukujitsu[5] = toshi + this.spliter + "4" + this.spliter + "29";
		// 憲法記念日(5月3日)
		syukujitsu[6] = toshi + this.spliter + "5" + this.spliter + "3";
		// みどりの日(5月4日)
		syukujitsu[7] = toshi + this.spliter + "5" + this.spliter + "4";
		// こどもの日(5月5日)
		syukujitsu[8] = toshi + this.spliter + "5" + this.spliter + "5";
		// 海の日(7月第3月曜日)
		nichi = this.howDay(toshi, 7, 3, 1);
		syukujitsu[9] = 
			toshi + this.spliter + "7" + this.spliter + nichi;
		// 山の日(8月11日)
		syukujitsu[10] = 
			toshi + this.spliter + "8" + this.spliter + "11";
		// 敬老の日(9月第3月曜日)
		nichi = this.howDay(toshi, 9, 3, 1);
		syukujitsu[11] = 
			toshi + this.spliter + "9" + this.spliter + nichi;
		// 秋分の日(前年の2月1日官報に掲載)・簡易計算法
		const aki =
			Math.floor(
				23.2488+0.242194*(toshi-1980)-Math.floor((toshi-1980)/4));
		syukujitsu[12] = toshi + this.spliter + "9" + this.spliter + aki;
		// 体育の日(10月第2月曜日)
		nichi = this.howDay(toshi, 10, 2, 1);
		syukujitsu[13] = 
			toshi + this.spliter + "10" + this.spliter + nichi;
		// 文化の日(11月3日)
		syukujitsu[14] = 
			toshi + this.spliter + "11" + this.spliter + "3";
		// 勤労感謝の日(11月23日)
		syukujitsu[15] = 
			toshi + this.spliter + "11" + this.spliter + "23";
		// 平成天皇誕生日(12月23日) *************************************
		if(toshi <= 2018) {
		 syukujitsu[16] = 
			toshi + this.spliter + "12" + this.spliter + "23";
		}

		/* 振替休日(祝日が日曜日の場合の月曜日) */
		// 元日(1月1日)
		syukuDay = new Date(toshi, 0, 1);
		if (syukuDay.getDay() === 0) {
			syukujitsu[17] = 
				toshi + this.spliter + "1" + this.spliter + "2";
		}
		// 建国記念日(2月11日)
		syukuDay = new Date(toshi, 1, 11);
		if (syukuDay.getDay() === 0) {
			syukujitsu[18] = 
				toshi + this.spliter + "2" + this.spliter + "12";
		}
		// 新天皇誕生日(2月23日)
		if(toshi >= 2020) {
			syukuDay = new Date(toshi, 1, 23);
			if (syukuDay.getDay() === 0) {
				syukujitsu[19] = 
					toshi + this.spliter + "2" + this.spliter + "24";
			}
		}
		// 春分の日
		syukuDay = new Date(toshi, 2, haru);
		if (syukuDay.getDay() === 0) {
			const haruDay = parseInt(haru) + 1;
			syukujitsu[20] = 
				toshi + this.spliter + "3" + this.spliter + haruDay;
		}
		// 昭和の日(4月29日)
		syukuDay = new Date(toshi, 3, 29);
		if (syukuDay.getDay() === 0) {
			syukujitsu[21] = 
				toshi + this.spliter + "4" + this.spliter + "30";
		}
		// 憲法記念日(5月3日)
		syukuDay = new Date(toshi, 4, 3);
		if (syukuDay.getDay() === 0) {
			syukujitsu[22] = 
				toshi + this.spliter + "5" + this.spliter + "6";
		}
		// みどりの日(5月4日)
		syukuDay = new Date(toshi, 4, 4);
		if (syukuDay.getDay() === 0) {
			syukujitsu[23] = 
				toshi + this.spliter + "5" + this.spliter + "6";
		}
		// こどもの日(5月5日)
		syukuDay = new Date(toshi, 4, 5);
		if (syukuDay.getDay() === 0) {
			syukujitsu[24] = 
				toshi + this.spliter + "5" + this.spliter + "6";
		}
		// 山の日(8月11日)
		syukuDay = new Date(toshi, 7, 11);
		if (syukuDay.getDay() === 0) {
			syukujitsu[25] = 
				toshi + this.spliter + "8" + this.spliter + "12";
		}
		// 秋分の日
		syukuDay = new Date(toshi, 8, aki);
		if (syukuDay.getDay() === 0) {
			const akiDay = parseInt(aki) + 1;
			syukujitsu[26] = 
				toshi + this.spliter + "9" + this.spliter + akiDay;
		}
		// 文化の日(11月3日)
		syukuDay = new Date(toshi, 10, 3);
		if (syukuDay.getDay() === 0) {
			syukujitsu[27] = 
				toshi + this.spliter + "11" + this.spliter + "4";
		}
		// 勤労感謝の日(11月23日)
		syukuDay = new Date(toshi, 10, 23);
		if (syukuDay.getDay() === 0) {
			syukujitsu[28] = 
				toshi + this.spliter + "11" + this.spliter + "24";
		}
		// 平成天皇誕生日(12月23日)
		if(toshi <= 2018) {
			syukuDay = new Date(toshi, 11, 23);
			if (syukuDay.getDay() === 0) {
				syukujitsu[29] = 
					toshi + this.spliter + "12" + this.spliter + "24";
			}
		}

		/* その前後が祝日の場合の休日(祝日にはさまれた平日) */
		const keiro = parseInt(syukujitsu[12].substr(7, 2));// 敬老の日
		const syubun = parseInt(syukujitsu[15].substr(7, 2));// 秋分の日
		if (syubun - keiro === 2) {
		 keiro++;
		 syukujitsu[30] = 
				toshi + this.spliter + "9" + this.spliter + keiro;
		}

		/* オリンピックの特例 *************************/
		if(toshi === 2020) {
			// 海の日
			syukujitsu[9] = 
				toshi + this.spliter + "7" + this.spliter + "23";
			// スポーツ(体育)の日
			syukujitsu[13] = 
				toshi + this.spliter + "7" + this.spliter + "24";
			// 山の日
			syukujitsu[10] = 
				toshi + this.spliter + "8" + this.spliter + "10";
		}
		/* 皇太子即位の特例 *************************/
		syukujitsu[31] = 
			"2019" + this.spliter + "4" + this.spliter + "30";
		syukujitsu[32] = 
			"2019" + this.spliter + "5" + this.spliter + "1";
		syukujitsu[33] = 
			"2019" + this.spliter + "5" + this.spliter + "2";
		syukujitsu[34] = 
			"2019" + this.spliter + "10" + this.spliter + "22";

		// 指定の日が祝日(or休日)かを判定
		for (let j = 0; j <= 30; j++) {
			syuku_cnt =
				syukujitsu.indexOf(
					toshi + this.spliter + tsuki + this.spliter + hi);
		}
		// 祝日の配列インデックスを返す -1は祝日でない
		return syuku_cnt;
	}

また、祝休日には第n番目の曜日となる場合もあるので、「第n番目の曜日の日付を求める」メソッドも追加しました。


	howDay(year, month, n, WantDayWeek) {
		let nichi;
		const FirstDay = new Date(year, month-1, 1);
		// 1日の曜日
		const FirstDayWeek = FirstDay.getDay();
		// 求めたい日付(0かプラスの場合)
		if ((WantDayWeek - FirstDayWeek) >= 0) {
			nichi = 1 + (WantDayWeek - FirstDayWeek) + 7 * (n - 1);
		}
		// 求めたい日付(マイナスの場合)
		if ((WantDayWeek - FirstDayWeek) < 0) {
			nichi = 1 + (WantDayWeek - FirstDayWeek) + (7 * n);
		}
		return nichi;
	}

各メソッド(閏年の判定)

上の項で使用された閏年を得るメソッドは次のとおり。


	isLeapYear(year) {
		if(((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
	{
			return true;
		} else {
			return false;
		}
	}

各メソッド(選択されている日付を得るメソッド)

現在選択されている日付を得るメソッドです。
引数が1のときは月日が一桁表示、それ以外は二桁表示の日付が返されます。


	getCurrentDay(fm) {
		// 一桁表示
		if(fm === 1) {
			return this.currentDay;
		// それ以外は二桁表示
		} else {
			const yymmdd = this.currentDay.split(this.spliter);
			// yymmdd[0]	年
			// yymmdd[1]	月の一桁表示
			// yymmdd[2]	日の一桁表示
			if (yymmdd[1] < 10) {yymmdd[1] = '0' + yymmdd[1];}
			if (yymmdd[2] < 10) {yymmdd[2] = '0' + yymmdd[2];}
			return 
			 yymmdd[0] + this.spliter + yymmdd[1] + this.spliter + yymmdd[2];
		}
	}

各メソッド(スプリッタを変更するためのメソッド)

スプリッタ(年月日の区切り文字)を変更したい場合もあります。そのためのメソッドです。


	setSpliter(spliterStr) {
		// 年月日の区切り文字
		this.spliter = spliterStr;
		// 現在選択されている日付
		this.currentDay =
		 this.nenn + this.spliter + this.tuki + this.spliter + this.nichi;
	}

各メソッド(カレンダーを表示するメソッド)

MkenCalendarのインスタンスを得て、このメソッドを呼び出すことでカレンダー表示されます。


	showCalendar() {
		// calendar枠作成と各マスのid.classをセット
		this.createHTML();
		// 日付の埋め込み
		this.setDays();
		// スタイルをセット
		this.setStyle();
		// イベントをセット
		this.setEvent();
	}

各メソッド(指定した日付にジャンプするメソッド)

遠く離れた日付に移動したいこともあるので、その日付にジャンプするメソッドを準備しました。


	removeDate(nenn, tuki, nichi) {
		this.nenn = nenn;
		this.tuki = tuki;
		this.nichi = nichi;
		this.currentDay =
		 this.nenn + this.spliter + this.tuki + this.spliter + this.nichi;
		this.setDays();
		this.setStyle();
	}
} // クラスの最後

簡単な使い方

このカレンダーのクラスファイルを読み込む為に、読み込む側のjsファイル(例:yomikomu.js)の <script>タグを置きます。
その<script>タグのtype=を"text/javascript"ではなく"module"にします。


<script type="module" src="yomikomu.js"></script>

これで読み込む側のjsファイル(例:yomikomu.js)内でimportできることになります。

HTML内のカレンダーを表示したいところにブロックレベル要素を置きます。


<div id="calendar1"></div>

この例ではcalendar1がカレンダーを表示したい場所です。


#calendar1 {
	/* カレンダー本体のサイズ */
	width: 259px;
	/* カレンダー本体のサイズ */
	height: 209px;
	padding: 10px;
	margin: 20px auto 0 50px;
	background-color: #aaaaaa;
}

calendar1のCSSです。

そして次が読み込む側のjsファイル(yomikomu.js)の内容です。
(1) カレンダークラスをimportし (2) インスタンスを作成し (3) showCalendarで表示 (4) 日付クリックイベントを記述 しています。


// カレンダークラスをimport(1)
import {MkenCalendarClass} from "./mkenCalendarClass.js";

const tagStr = "calendar1";
// カレンダーのインスタンス(2)
const cal = new MkenCalendarClass(tagStr);

// カレンダーの表示(3)
cal.showCalendar();
// 二桁表示
alert("今日は" + cal.getCurrentDay() + " です");

// カレンダーの日付クリックイベント(4)
$("." + tagStr + "DayCLS").on("click", function(){
	// 一桁表示
	alert(cal.getCurrentDay(1) + " がクリックされました");

// 2013年4月15日に移動する場合
cal.removeDate(2013, 4, 15);
 

カレンダーを表示した直後は今日の年月日がプロパティに入っています。

クラスに書き換えてみて、コードがすっきりしたような気がします