初めに
javascriptでおしゃれなカレンダーを作ろう取り組んだコードを残します。
完成品
構成
構成は一般的なjavascriptの構成ですので、index.html、style.css、calendar.jsです。
カスタマイズしやすいようにclass名等を細かく決めています。
色々挑戦したので不要なコードも残っているかと思いますがお許しください。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>カレンダー</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container">
<div class="content">
<button id="year_button"></button>
<button id="year_prev">▲</button>
<button id="year_next">▼</button>
<button id="prev">◀◀</button>
<button id="next">▶▶</button>
<div id="calendar"></div>
<div id="modal" style="display: none">
<div class="modal-container">
<select id="year-select"></select>
<button id="modal-ok">OK</button>
<button id="modal-cancel">Cancel</button>
</div>
</div>
</div>
</div>
<script src="calendar.js"></script>
</body>
</html>
style.css
/* カレンダーのテーブル全体 */
.container {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
padding: 10px;
}
.content {
position: relative;
width: 400px;
height: 360px;
padding: 10px;
font-size: 18px;
font-style: italic;
margin: 10px auto;
justify-content: center;
}
#calendar {
margin: 0 auto 10px;
padding: 0;
}
button#year_button {
position: absolute;
font-size: 30px;
border: none;
padding: 1px 15px 0;
border-radius: 5px;
background-color: #0074d0;
color: white;
font-style: italic;
top: 30px;
left: 12px;
box-shadow: 3px 3px 3px -3px black;
transition: background-color 0.3s ease, color 0.3s ease;
}
button#year_button:hover {
background-color: #6df049;
}
button#prev,
button#next {
font-size: 20px;
font-style: italic;
position: absolute;
color: #0074d0;
background-color: transparent;
border: none;
top: 54px;
right: 64px;
}
button#next {
right: 8px;
}
button#prev:hover,
button#next:hover {
color: #6df049;
}
button#year_prev,
button#year_next {
font-size: 12px;
font-style: italic;
position: absolute;
transform: scale(5, 1);
color: #0074d0;
background-color: transparent;
border: none;
top: 15.2px;
left: 70px;
}
button#year_next {
top: 65.5px;
}
button#year_prev:hover,
button#year_next:hover {
color: #6df049;
}
table {
border-collapse: collapse;
background-color: rgb(255, 255, 255);
width: 100%;
margin: 80px auto;
position: relative;
padding-top: 50px;
}
table tr:first-child td {
background-color: #0074d0;
font-size: 18px;
color: white !important;
font-style: italic;
}
table .buttons {
display: flex;
gap: 10px;
}
td {
text-align: center;
padding: 4px;
font-size: 18px;
height: 30px;
border: 2px solid #b9c7da;
width: 14.28%;
position: relative;
}
td.holiday {
color: #e41414;
}
td.clickable {
cursor: pointer;
transition: background-color 0.3s;
}
td.clickable:hover {
background-color: rgb(92, 238, 131);
}
table td:first-child {
color: #e41414;
}
table td:nth-child(7) {
color: #1152c1;
}
/* モーダルのスタイル */
#modal {
position: absolute;
width: 200px;
height: 51px;
margin-top: 0;
font-size: 24px;
background-color: white;
border: solid 4px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
top: 18.5px;
z-index: 2;
}
select#year-select {
font-size: 21px;
font-style: italic;
background-color: transparent;
border: none;
}
button#modal-ok,
button#modal-cancel {
border: none;
background-color: #0074d0;
color: white;
font-size: 19px;
padding: 1px 7px 1px 3px !important;
font-style: italic;
border-radius: 5px;
}
button#modal-cancel {
background-color: red;
}
calendar.js
let holidays = {}; // 祝日データを格納するオブジェクト
// 国民の祝日 API から祝日データを取得
async function fetchHolidays(year) {
const response = await fetch(
`https://holidays-jp.github.io/api/v1/date.json`
);
if (response.ok) {
const data = await response.json();
// 指定された年の祝日を抽出
for (const [date, name] of Object.entries(data)) {
if (date.startsWith(year)) {
holidays[date] = name;
}
}
} else {
console.error("祝日データの取得に失敗しました。");
}
}
// カレンダーを生成する関数
async function renderCalendar(year, month) {
// 祝日データを取得(まだ未取得の場合のみ)
if (Object.keys(holidays).length === 0) {
await fetchHolidays(year);
}
const startDate = new Date(year, month - 1, 1); // 月の最初の日
const endDate = new Date(year, month, 0); // 月の末日
const endDayCount = endDate.getDate(); // 月の日数
const startDay = startDate.getDay(); // 月の最初の曜日
let dayCount = 1; // 日にちカウンター
let calendarHtml = ""; // カレンダーHTML
// calendarHtml += "<h1>" + year + "/" + month + "</h1>";
calendarHtml += "<table>";
calendarHtml += "<tr>";
const weeks = ["Sun", "Mon", "The", "Wed", "Thu", "Fri", "Sat"];
for (let i = 0; i < weeks.length; i++) {
calendarHtml += "<td>" + weeks[i] + "</td>";
}
calendarHtml += "</tr>";
for (let w = 0; w < 6; w++) {
calendarHtml += "<tr>";
for (let d = 0; d < 7; d++) {
if (w == 0 && d < startDay) {
calendarHtml += "<td></td>"; // 月初まで空白を埋める
} else if (dayCount > endDayCount) {
calendarHtml += "<td></td>"; // 月末を超えた場合
} else {
const currentDate = `${year}-${String(month).padStart(2, "0")}-${String(
dayCount
).padStart(2, "0")}`;
let className = "clickable";
if (d === 0) className += " sunday"; // 日曜日
if (d === 6) className += " saturday"; // 土曜日
if (holidays[currentDate]) className += " holiday"; // 祝日
calendarHtml += `<td class="${className}" data-date="${currentDate}"><span class="date">${dayCount}</span></td>`;
dayCount++;
}
}
calendarHtml += "</tr>";
}
calendarHtml += "</table>";
calendarHtml += "<tr class='calendar-header'>";
// ここでボタンに年と月を設定
const yearButton = document.getElementById("year_button");
yearButton.textContent = `${year} / ${month}`; // 年と月をボタンに表示
document.querySelector("#calendar").innerHTML = calendarHtml;
// 最終行が空であれば非表示にする
const rows = document.querySelectorAll("#calendar table tr");
const lastRow = rows[rows.length - 1];
let isEmpty = true;
lastRow.querySelectorAll("td").forEach((cell) => {
if (cell.textContent.trim() == "") {
isEmpty = true;
}
});
if (isEmpty) {
lastRow.style.display = "none";
}
const firstRow = rows[1];
const firstCell = firstRow.querySelector("td");
const lastRow2 = rows[rows.length - 2];
if (firstCell.textContent.trim() !== "" && endDayCount === 28) {
console.log(firstCell.textContent.trim());
lastRow2.style.display = "none";
}
// 日付をクリックしたときのイベント
document.querySelectorAll(".clickable").forEach((cell) => {
cell.addEventListener("click", (e) => {
const targetCell = e.target.closest("td");
const selectedDate = targetCell?.dataset.date;
if (holidays[selectedDate]) {
alert(
`選択された日付: ${selectedDate}\n祝日: ${holidays[selectedDate]}`
);
} else {
alert(`選択された日付: ${selectedDate}`);
}
});
});
}
// 初期化
document.addEventListener("DOMContentLoaded", async () => {
const today = new Date();
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
document.getElementById("prev").addEventListener("click", async () => {
today.setMonth(today.getMonth() - 1);
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
});
document.getElementById("next").addEventListener("click", async () => {
today.setMonth(today.getMonth() + 1);
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
});
//一年移動
document.getElementById("year_prev").addEventListener("click", async () => {
today.setFullYear(today.getFullYear() - 1);
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
});
document.getElementById("year_next").addEventListener("click", async () => {
today.setFullYear(today.getFullYear() + 1);
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
});
// モーダル内のイベントを設定
document.getElementById("year_button").addEventListener("click", openModal);
console.log(document.getElementById("year_button"));
// 年選択肢を生成する関数
function populateYearOptions() {
const yearSelect = document.getElementById("year-select");
const currentYear = new Date().getFullYear();
if (!yearSelect) {
console.error("Year select element not found.");
return;
}
console.log(yearSelect);
yearSelect.innerHTML = ""; // 初期化
for (let year = 2100; year >= 1924; year--) {
const option = document.createElement("option");
option.value = year;
option.textContent = year;
// 現在の年を初期値として選択
if (year === currentYear) {
option.selected = true;
}
yearSelect.appendChild(option);
}
}
// ページロード時に年選択肢を生成
document.addEventListener("DOMContentLoaded", () => {
populateYearOptions();
});
populateYearOptions();
document.getElementById("modal-ok").addEventListener("click", async () => {
const yearSelect = document.getElementById("year-select");
const selectedYear = parseInt(yearSelect.value, 10);
if (!isNaN(selectedYear)) {
today.setFullYear(selectedYear); // 選択した年に移動
await renderCalendar(today.getFullYear(), today.getMonth() + 1);
closeModal(); // モーダルを閉じる
} else {
alert("有効な年を選択してください");
}
});
document.getElementById("modal-cancel").addEventListener("click", closeModal);
// モーダルを開く関数
function openModal() {
document.getElementById("modal").style.display = "flex";
}
// モーダルを閉じる関数
function closeModal() {
document.getElementById("modal").style.display = "none";
}
});