JavaScriptで書いた、Webページにカレンダーを表示するプログラムです。
GitHubにも置いてあります。
https://github.com/kmaebashi/calendar
実際に動くものを貼っておきます。以下です。
――Web画面に表示する「カレンダー」というのは、「自力で作れば大した苦労もなく作れるのに、なぜかみんな外部ライブラリを使いたがる」機能の代表格かと思います。まあ、それでもこれだけ簡単に作れるものなので、検索すると「作ってみました」系の記事は結構出てきます。ただ、「作ってみました」系のページにある実装だと、HTMLを文字列で組み立ててinnerHTMLに設定していたり(好みの問題かもしれませんが、ここはDOMを組み立てるべきだと思う)、あまりうまく部品化できていないように感じられるものが多かったりしたので、新たな実装を加えることにもいくらかは価値があるでしょう。
Calendarクラスとして提供しています。
<script type="text/javascript" src="./calendar.js"></script> <link rel="stylesheet" type="text/css" href="calendar.css">
<div id="calendar"></div>
const elem = document.getElementById("calendar"); const calendar = new Calendar(elem, new Date());
calendar.setDatePickedCallback(clicked); ... function clicked(date) { alert("日付がクリックされました.." + date.toString()); }
const date = calendar.getCurrentDate();
実際の例については、このページの末尾に貼ってあるtest.htmlか、このページ自体のHTMLを参照してください。
calendar.js
class Calendar { constructor(targetElement, date) { this.targetElement = targetElement; this.date = date; this.render(); } setDatePickedCallback(callback) { this.datePickedCallback = callback; } getCurrentDate() { return this.date; } render() { while (this.targetElement.firstChild ){ this.targetElement.removeChild(this.targetElement.firstChild); } const firstYoubi = Calendar.#getFirstYoubi(this.date); let nth = 0; // 月の中の何日目かを示す let lastNth = Calendar.#getLastNth(this.date); let endFlag = false; const tableElem = document.createElement("table"); const headTr = document.createElement("tr"); const leftArrowTd = document.createElement("td"); leftArrowTd.innerText = "≪"; leftArrowTd.classList.add("calendar-left-arrow"); headTr.appendChild(leftArrowTd); const monthTd = document.createElement("td"); monthTd.colSpan = 5; monthTd.innerText = this.date.getFullYear() + "年" + (this.date.getMonth() + 1) + "月"; monthTd.classList.add("calendar-header-month"); headTr.appendChild(monthTd); const rightArrowTd = document.createElement("td"); rightArrowTd.innerText = "≫"; rightArrowTd.classList.add("calendar-right-arrow"); headTr.appendChild(rightArrowTd); tableElem.appendChild(headTr); leftArrowTd.onclick = this.leftArrowClicked.bind(this); rightArrowTd.onclick = this.rightArrowClicked.bind(this); for (;;) { const trElem = document.createElement("tr"); tableElem.appendChild(trElem); for (let youbi = 0; youbi < 7; youbi++) { const tdElem = document.createElement("td"); trElem.appendChild(tdElem); if (nth == 0 && youbi < firstYoubi) { ; } else if (nth <= lastNth) { if (nth == 0 && youbi == firstYoubi) { nth = 1; } tdElem.innerText = "" + nth; tdElem.setAttribute("data-date", nth); tdElem.classList.add("calendar-date"); if (youbi == 0) { tdElem.classList.add("calendar-sunday"); } if (youbi == 6) { tdElem.classList.add("calendar-saturday"); } if (nth == this.date.getDate()) { tdElem.classList.add("calendar-target-date"); } tdElem.onclick = this.dateClicked.bind(this); nth++; if (nth > lastNth) { endFlag = true; } } else { ; } } if (endFlag) { break; } } this.targetElement.appendChild(tableElem); } leftArrowClicked() { let newYear = this.date.getFullYear(); let newMonth; let newDate; if (this.date.getMonth() == 0) { newMonth = 11; newYear--; } else { newMonth = this.date.getMonth() - 1; } newDate = Calendar.#fixLastDate(newYear, newMonth, this.date.getDate()); this.date = new Date(newYear, newMonth, newDate); this.render(); } rightArrowClicked() { let newYear = this.date.getFullYear(); let newMonth; let newDate; if (this.date.getMonth() == 11) { newMonth = 0; newYear++; } else { newMonth = this.date.getMonth() + 1; } newDate = Calendar.#fixLastDate(newYear, newMonth, this.date.getDate()); this.date = new Date(newYear, newMonth, newDate); this.render(); } dateClicked(e) { const date = e.target.dataset.date; this.date.setDate(parseInt(date)); this.render(); if (this.datePickedCallback !== undefined && this.datePickedCallback !== null) { this.datePickedCallback(this.date); } } static #getLastNth(date) { const date2 = new Date(date.getTime()); date2.setMonth(date.getMonth() + 1, 0); return date2.getDate(); } static #getFirstYoubi(date) { const date2 = new Date(date.getFullYear(), date.getMonth(), date.getDate()); date2.setDate(1); return date2.getDay(); } static #fixLastDate(newYear, newMonth, oldDate) { const tempDate = new Date(newYear, newMonth, 1); const lastNth = Calendar.#getLastNth(tempDate); let newDate; if (oldDate > lastNth) { newDate = lastNth; } else { newDate = oldDate; } return newDate; } }
解説……というほどのものでもありませんが。
デザインを規定しているCSSは以下。見た目を変えたければここをいじってください。
calendar.css
td.calendar-left-arrow { cursor: pointer; text-align: center; } td.calendar-header-month { text-align: center; } td.calendar-right-arrow { cursor: pointer; text-align: center; } td.calendar-date { cursor: pointer; text-align: center; } td.calendar-saturday { background-color: #ccccff; } td.calendar-sunday { background-color: #ffcccc; } td.calendar-target-date { background-color: #ffffcc; }
JavaScript側を見てもわかるとおり、このカレンダーの「≪」や「YYYY年M月」や「≫」は、カレンダー本体のテーブル要素の一部です(YYYY年M月部分は、colspanで5列ぶち抜いています)。
テスト用のHTMLであるtest.htmlも一応載せておきます。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>カレンダーのテスト</title> <script type="text/javascript" src="./calendar.js"></script> <link rel="stylesheet" type="text/css" href="calendar.css"> </head> <body> <div id="calendar"></div> <p><button onclick="currentDate()">今の日付</button><span id="current-date"></span></p> <script> function clicked(date) { alert("日付がクリックされました.." + date.toString()); } const elem = document.getElementById("calendar"); const calendar = new Calendar(elem, new Date()); calendar.setDatePickedCallback(clicked); function currentDate() { const date = calendar.getCurrentDate(); const span = document.getElementById("current-date"); span.innerText = "今選ばれている日付.." + date.toString(); } </script> </body> </html>
公開日: 2024/03/31
不具合等ありましたら、掲示板にご連絡願います。