【JavaScript】カレンダーを幾つかGitHubPagesで公開してみた


# カレンダーアプリ案を幾つか

春なのでカレンダーを作ってみました。
取り敢えず新人への課題や、新しい言語に触れた時のお題になるような気がしています。

- HTML / CSS / JavaScript（vanillaJS）のフロントエンドのみの構成
- 曜日の指定方法は2通り。7ごとに改行、grid
＃4/8 8:14追記　コメントで、配列の利用案をいただきました。是非確認してみてください。
- 整列方法は3通り。テーブル、等幅フォント、grid
- 自前で閏年計算するパターンのあります
- スタイルを徐々に設定してみるパートもあります
- 100年カレンダーも作成


---

# デモ

https://kubo-4ka.github.io/calendars-js/


# ソース

https://github.com/kubo-4ka/calendars-js

---

##  １．7ごとに改行し、htmlのtableを利用する案

```html
<!DOCTYPE html>
<html lang="ja">
<head><meta charset="UTF-8"><title>カレンダー</title></head>
<body>
  <h1 id="today"></h1>
  <table border="1">
    <thead>
      <tr><th>日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th>土</th></tr>
    </thead>
    <tbody id="calbody"></tbody>
  </table>
  <script>
    const t = new Date(), y = t.getFullYear(), m = t.getMonth(), d = t.getDate();
    document.getElementById("today").textContent = `${y}/${m+1}/${d}`;
    const first = new Date(y, m, 1).getDay(), last = new Date(y, m+1, 0).getDate();
    const body = document.getElementById("calbody");
    let row = document.createElement("tr");
    for(let i=0;i<first;i++) row.appendChild(document.createElement("td"));
    for(let day=1; day<=last; day++){
      const td = document.createElement("td");
      td.textContent = day;
      row.appendChild(td);
      if((first + day) % 7 === 0 || day === last){
        body.appendChild(row);
        row = document.createElement("tr");
      }
    }
  </script>
</body>
</html>
```

https://kubo-4ka.github.io/calendars-js/this_month_html_table/index.html

- new Date(年, 月, 1) で1日の曜日を取得
- getDay() で曜日番号（0:日〜6:土）を取得
- new Date(年, 月, 0) を使うと「前の月の末日」が取得できます（便利！）
- 月初の空白は `<td></td>`で埋める
- 日付を順番に追加
- 「(first + day) % 7 === 0」で、剰余を利用して7列ごと改行、または月末で改行（新しい`<tr>`）
- 何となく懐かしい見栄えになります

---
##  ２．7ごとに改行し、等幅フォントを利用する案

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>カレンダー</title>
  <style>
    #calendar {
      font-family: monospace;
      white-space: pre;
    }
  </style>
</head>
<body>
  <h1 id="today"></h1>
  <div id="calendar"></div>
  <script>
    const t = new Date(), y = t.getFullYear(), m = t.getMonth(), d = t.getDate();
    document.getElementById("today").textContent = `${y}/${m+1}/${d}`;
    const cal = document.getElementById("calendar");
    const w = ["日","月","火","水","木","金","土"];
    cal.innerHTML = w.join(" ") + "\n";
    const first = new Date(y, m, 1).getDay();
    let row = Array(first).fill("  ");
    const last = new Date(y, m+1, 0).getDate();
    for(let i=1;i<=last;i++){
      row.push(i.toString().padStart(2, ' '));
      if(row.length === 7){
        cal.innerHTML += row.join(" ") + "\n";
        row = [];
      }
    }
    if(row.length) cal.innerHTML += row.join(" ");
  </script>
</body>
</html>
```

https://kubo-4ka.github.io/calendars-js/this_month_len7ln/index.html

- monospace	文字の幅がすべて同じなので列が揃う
- white-space: pre	JavaScriptの \n や " " をそのままHTML上に反映
- padStart(2, ' ')	数字を2桁に揃える工夫（桁ずれ防止）
- first = getDay()	月初の曜日を取得して、空白を調整
- 何となく寂しい見栄えになります。何かスタイルを補いたくなります

---

##  ３．gridを利用する案
この案をベースに、閏年自前計算と、スタイルの変化を試してみます。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>カレンダー</title>
  <style>
    #calendar {
      display: grid;
      grid-template-columns: repeat(7, 1fr);
      max-width: 400px;
    }
    #calendar div {
      text-align: center;
      padding: 4px;
    }
  </style>
</head>
<body>
  <h1 id="today"></h1>
  <div id="calendar"></div>
  <script>
    const today = new Date(), y = today.getFullYear(), m = today.getMonth(), d = today.getDate();
    document.getElementById("today").textContent = `${y}/${m + 1}/${d}`;
    const cal = document.getElementById("calendar"), w = ["日","月","火","水","木","金","土"];
    cal.innerHTML = w.map(v=>`<div>${v}</div>`).join("");
    const first = new Date(y, m, 1).getDay(), last = new Date(y, m + 1, 0).getDate();
    for(let i=0;i<first;i++) cal.innerHTML += `<div></div>`;
    for(let i=1;i<=last;i++) cal.innerHTML += `<div>${i}</div>`;
  </script>
</body>
</html>
```

https://kubo-4ka.github.io/calendars-js/this_month_grid/index.html

- HTML構成
  - `<h1 id="today">`
→ JavaScriptで今日の日付（yyyy/mm/dd）を表示するための要素。

  - `<div id="calendar">`
→ カレンダー（曜日と日付）をすべてグリッド表示するコンテナ。

- CSSスタイル
  - #calendar に display: grid を設定
→ 7列（曜日）で整列表示するための基本設定。
  - grid-template-columns: repeat(7, 1fr)
→ 7列均等割りで曜日ごとの列幅を確保。
  - max-width: 400px
→ カレンダーの幅が広くなりすぎないよう制限。
  - #calendar div に text-align: center と padding
→ 日付や曜日を中央揃え＆少し余白を確保して見やすくする。

- JavaScriptの処理
  - const today = new Date()
  → 現在の日付情報を取得。
  - y, m, d
→ 年（y）、月（m）、日（d）をそれぞれ取得。
  - document.getElementById("today").textContent = ...
→ 取得した年月日を画面上部に表示。
  - const w = ["日", "月", ...]
→ 曜日を表す文字列を準備。
  - w.map(...).join("")
→ 曜日を `<div>` 要素としてカレンダーに挿入。
  - first = new Date(y, m, 1).getDay()
→ 今月1日の**曜日（0〜6）**を取得。
  - last = new Date(y, m+1, 0).getDate()
→ 今月の**最終日（日数）**を取得。
  - for (let i = 0; i < first; i++)
→ 月初までの空白セルを挿入（例：1日が水曜なら空白が2つ必要）。
  - for (let i = 1; i <= last; i++)
→ 実際の日付を `<div>` に1日ずつ入れてカレンダーを完成。


---

##  ３－１．閏年を自前で計算する案
見栄えは変えずに、判定ロジックを変化させてみます。
```diff
$ diff this_month_grid\index.html this_month_grid_self_leap_chk\index.html -u
--- "this_month_grid\\index.html"       2025-04-06 22:28:13.800090600 +0900
+++ "this_month_grid_self_leap_chk\\index.html" 2025-04-07 06:45:44.106784600 +0900
@@ -23,7 +23,27 @@
     document.getElementById("today").textContent = `${y}/${m + 1}/${d}`;
     const cal = document.getElementById("calendar"), w = ["日","月","火","水","木","金","土"];
     cal.innerHTML = w.map(v=>`<div>${v}</div>`).join("");
-    const first = new Date(y, m, 1).getDay(), last = new Date(y, m + 1, 0).getDate();
+
+    // ==== 自前で曜日計算 ====
+    function isLeapYear(y) {
+      return y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0);
+    }
+    function getDaysInMonth(y, m) {
+      if (m === 1) return isLeapYear(y) ? 29 : 28;
+      return [31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m];
+    }
+    function countDaysSince1970(y, m, d) {
+      let days = 0;
+      for (let yy = 1970; yy < y; yy++) {
+        days += isLeapYear(yy) ? 366 : 365;
+      }
+      for (let mm = 0; mm < m; mm++) {
+        days += getDaysInMonth(y, mm);
+      }
+      return days + (d - 1);
+    }
+    const first = (countDaysSince1970(y, m, 1) + 4) % 7;
+    const last = getDaysInMonth(y, m);
     for(let i=0;i<first;i++) cal.innerHTML += `<div></div>`;
     for(let i=1;i<=last;i++) cal.innerHTML += `<div>${i}</div>`;
   </script>
```

https://kubo-4ka.github.io/calendars-js/this_month_grid_self_leap_chk/index.html

- first（月初の曜日）を Date で取得する代わりに、自前で算出
- 関数 isLeapYear, getDaysInMonth, countDaysSince1970 を追加
- 見た目・CSS・HTML構造など他部分は変更せずdiffが取れるように、必要最低限の変更に留める

### 閏年について

- 閏年の判定ルール（西暦）
  - 4で割り切れる年は閏年
  - ただし、100で割り切れる年は平年
  - ただし、400で割り切れる年はやっぱり閏年

```JavaScript
return y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0);
```

| 条件 | 結果 | 例 |
|---|---|---|
| 年 % 400 === 0 | 閏年 | 1600, 2000, 2400 |
| 年 % 100 === 0 | 平年 | 1700, 1800, 1900 |
| 年 % 4 === 0 | 閏年 | 1996, 2004, 2020 |
| 上記以外 | 平年 | 2023, 2025 |

```JavaScript
year % 4 === 0         // 4の倍数であること（まず候補になる）
&& (
  year % 100 !== 0     // 100の倍数でない ＝ 閏年確定
  || year % 400 === 0  // 100の倍数でも400の倍数ならOK
)
```

### 閏年判定の背景（なぜこうなっている？）
地球の公転周期は、約365.2422日
→ 毎年0.2422日ずつズレる
そこで：
- 4年に1回 ＝ +1日（= +0.25×4）→ 補正される
- でも 0.25日では多すぎる → 100年に1回抜く（補正しすぎ防止）
- それでも 微妙に不足 → 400年に1回戻す
こうして 365.2425日に近づけています。

---

##  ３－２．ダークテーマの案
以降は見栄えを変化させていきます。
```diff
$ diff this_month_grid\index.html this_month_grid_dark\index.html -u
--- "this_month_grid\\index.html"       2025-04-06 22:28:13.800090600 +0900
+++ "this_month_grid_dark\\index.html"  2025-04-07 06:58:46.224252800 +0900
@@ -4,6 +4,12 @@
   <meta charset="UTF-8">
   <title>カレンダー</title>
   <style>
+    body {
+      background-color: #121212;
+      color: #ffffff;
+      font-family: sans-serif;
+      padding: 20px;
+    }
     #calendar {
       display: grid;
       grid-template-columns: repeat(7, 1fr);
@@ -12,6 +18,9 @@
     #calendar div {
       text-align: center;
       padding: 4px;
+      background-color: #1e1e1e;
+      border: 1px solid #333;
+      border-radius: 4px;
     }
   </style>
 </head>
```

https://kubo-4ka.github.io/calendars-js/this_month_grid_dark/index.html

| 項目 | 旧 | 新（ダークテーマ）|
|---|---|---|
| 背景 | 白 | 暗めのグレー（#121212）|
| 文字色 | 黒 | 白（#ffffff）|
| セル背景 | なし | ダークグレー（#1e1e1e）|
| セル枠 | なし | 黒寄りの線（#333）|
| フォント | 標準 | sans-serif 指定 |
| セル角丸 | なし | 角丸4px |
---

##  ３－３．中央配置の案

```diff
$ diff this_month_grid_dark\index.html this_month_grid_dark_center\index.html -u
--- "this_month_grid_dark\\index.html"  2025-04-07 06:58:46.224252800 +0900
+++ "this_month_grid_dark_center\\index.html"   2025-04-07 07:07:59.916219500 +0900
@@ -5,19 +5,29 @@
   <title>カレンダー</title>
   <style>
     body {
+      margin: 0;
+      padding: 20px;
       background-color: #121212;
       color: #ffffff;
       font-family: sans-serif;
-      padding: 20px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: flex-start;
+      min-height: 100vh;
     }
     #calendar {
       display: grid;
       grid-template-columns: repeat(7, 1fr);
       max-width: 400px;
+      width: 100%;
+      gap: 4px;
+      margin-top: 20px;
     }
     #calendar div {
       text-align: center;
-      padding: 4px;
+      aspect-ratio: 1 / 1;
+      line-height: 2.5em;
       background-color: #1e1e1e;
       border: 1px solid #333;
       border-radius: 4px;

```

https://kubo-4ka.github.io/calendars-js/this_month_grid_dark_center/index.html

- 日付（`<h1>`）が上部中央に配置
- カレンダーはその下に中央寄せで表示
- セルのサイズ・背景・色はそのまま


---

##  ３－４．今日をハイライトする案

```diff
$ diff this_month_grid_dark_center\index.html this_month_grid_dark_center_highlight_today\index.html -u
--- "this_month_grid_dark_center\\index.html"   2025-04-07 07:07:59.916219500 +0900
+++ "this_month_grid_dark_center_highlight_today\\index.html"   2025-04-07 07:59:52.931849300 +0900
@@ -32,6 +32,12 @@
       border: 1px solid #333;
       border-radius: 4px;
     }
+    #calendar div.today {
+      background-color: #004444;
+      border: 2px solid #00ffcc;
+      font-weight: bold;
+      color: #ffffff;
+    }
   </style>
 </head>
 <body>
@@ -44,7 +50,11 @@
     cal.innerHTML = w.map(v=>`<div>${v}</div>`).join("");
     const first = new Date(y, m, 1).getDay(), last = new Date(y, m + 1, 0).getDate();
     for(let i=0;i<first;i++) cal.innerHTML += `<div></div>`;
-    for(let i=1;i<=last;i++) cal.innerHTML += `<div>${i}</div>`;
+    for(let i=1;i<=last;i++) {
+      const isToday = y === today.getFullYear() && m === today.getMonth() && i === today.getDate();
+      const cls = isToday ? ' class="today"' : '';
+      cal.innerHTML += `<div${cls}>${i}</div>`;
+    }
   </script>
 </body>
 </html>
 ```

https://kubo-4ka.github.io/calendars-js/this_month_grid_dark_center_highlight_today/index.html

- セルの描画時に「今日」であればクラス追加
- CSSで .today クラスにだけ装飾


---

## ４．100年カレンダーの案
最後は、好きな年月を見れるようにしてみます。

https://kubo-4ka.github.io/calendars-js/100years/index.html

- HTML構成（index.html）
  - calendar-container：全体を囲むラッパー（中央寄せ用）
  - .header：年・月のプルダウンと、前月・今日・次月ボタン
  - .weekdays：曜日見出し（「日〜土」）
  - #calendar：実際の日付を表示する領域（JSで動的に更新）

- CSSスタイル（style.css）
  - body：ダークテーマ（背景黒＋文字白）
  - .calendar-container：幅を400pxに制限し、中央に配置
  - .header：ボタンやセレクトボックスを横並びに整列
  - select：月・年の選択ボックスの色や背景を暗く統一
  - .today：今日の日付だけハイライト（明るい枠＋背景色）
  - button:disabled：移動できないときのボタンを暗く非活性表示
  - .calendar-grid：7列のグリッドで日付を整列表示（gap付き）
  - .calendar-grid div：日付の各セルを正方形に（aspect-ratio: 1/1）

- JavaScriptの動作（script.js）
  - 初期化処理
    - currentDate = new Date()：最初に「今日」の日付を記憶
    - 年：1970年〜2069年をセレクトボックスに追加
    - 月：0〜11（表示は1月〜12月）をセレクトボックスに追加
  - カレンダー描画処理（updateCalendar()）
    - 選択中の年・月から、1日の曜日と末日を取得
    - カレンダー表示をいったん空にする（再描画のため）
    - 月初の曜日に合わせて空白のセルを追加（1日が水曜なら2つ空欄）
    - 1日〜末日までセルを追加
    - 今日の日付は .today クラスを付与（スタイルで強調）
  - ナビゲーション操作
    - 「←」ボタン：前月へ（1月なら前年の12月に切り替え）
    - 「→」ボタン：次月へ（12月なら翌年の1月に切り替え）
    - 「Today」ボタン：現在の年・月に戻る
    - セレクト変更時も、カレンダーを即更新
  - ボタンの無効化制御（updateNavigationButtons()）
    - 1970年1月より前に戻れない → 「←」無効
    - 2069年12月より先に進めない → 「→」無効

---