Building a Minimal Calendar with HTML, CSS, and JavaScript

Structure Overview

The layout centers a calendar grid within a fixed-width frame. A header displays the current month and year between navgiation controls. Below, a table lists weekday labels followed by date cells populated dynamically.

HTML Layout

<body>
  <div class="calendar-wrapper">
    <div class="calendar-container">
      | <div class="header"> <span class="nav prev">&lt;</span> <span class="month-label"></span> <span class="nav next">&gt;</span> </div> |
|---|
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
    </div>
  </div>
</body>

Styling

.calendar-wrapper {
  width: 440px;
  margin: 50px auto;
  position: relative;
}
.calendar-container {
  width: 420px;
  margin: 10px;
  position: absolute;
}
.header {
  height: 60px;
  text-align: center;
  line-height: 60px;
  position: relative;
}
.month-label {
  font-size: 30px;
}
.today {
  color: red;
  font-weight: bold;
}
table {
  background-color: #f7f7f7;
}
.weekday-row td {
  font-size: 15px;
}
td {
  width: 60px;
  height: 60px;
  text-align: center;
  font-family: Simsun;
  font-size: 20px;
}
.nav {
  width: 60px;
  height: 60px;
  position: absolute;
  top: 0;
}
.nav.prev { left: 0; }
.nav.next { right: 0; }
.nav:hover {
  background-color: rgba(30, 30, 30, 0.2);
}

Core Logic

Two utility functions handle date calculations:

Leap Year Check

function checkLeap(y) {
  return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
}

Weekday Determination

const monthLengths = [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function getWeekday(y, m, d = 1) {
  let total = (y - 1) * 365 + Math.floor((y - 1) / 4) - Math.floor((y - 1) / 100) + Math.floor((y - 1) / 400) + d;
  for (let idx = 0; idx < m - 1; idx++) {
    total += monthLengths[idx];
  }
  if (m > 2) {
    total += checkLeap(y) ? 29 : 28;
  }
  return total % 7; // 0 = Sunday, 1 = Monday, …
}

Rendering the Grid

function renderCalendar(y, m, startIdx) {
  const today = new Date();
  const isCurrent = y === today.getFullYear() && m === today.getMonth() + 1;
  
  let dim = 31;
  if (m === 2) {
    dim = checkLeap(y) ? 29 : 28;
  } else {
    dim = monthLengths[m - 1];
  }

  const monthLabel = document.querySelector('.month-label');
  monthLabel.textContent = `${y}年${m}月`;

  const grid = document.querySelector('.dates-grid');
  let html = '<tr>';

  for (let empty = 0; empty < startIdx; empty++) {
    html += '<td></td>';
  }

  let colCount = startIdx;
  for (let dayNum = 1; dayNum <= dim; dayNum++) {
    const cellCls = (isCurrent && dayNum === today.getDate()) ? 'today' : '';
    html += `<td class="${cellCls}">${dayNum}</td>`;
    colCount = (colCount + 1) % 7;
    if (colCount === 0 && dayNum < dim) html += '</tr><tr>';
  }

  while (colCount > 0 && colCount < 7) {
    html += '<td></td>';
    colCount++;
  }
  html += '</tr>';
  grid.innerHTML = html;
}

Initial Display

const present = new Date();
const yr = present.getFullYear();
const mo = present.getMonth() + 1;
renderCalendar(yr, mo, getWeekday(yr, mo));

Navigation Contorls

function shiftMonth(offset) {
  const label = document.querySelector('.month-label').textContent;
  const nums = [...label.matchAll(/\d+/g)].map(n => parseInt(n[0]));
  let [yr, mo] = [nums[0], nums[1] + offset];
  if (mo > 12) { mo = 1; yr++; }
  if (mo < 1) { mo = 12; yr--; }
  document.querySelector('.month-label').textContent = '';
  renderCalendar(yr, mo, getWeekday(yr, mo));
}

document.querySelector('.nav.next').addEventListener('click', () => shiftMonth(1));
document.querySelector('.nav.prev').addEventListener('click', () => shiftMonth(-1));

Tags: Calendar frontend javascript html css

Posted on Wed, 24 Jun 2026 18:19:26 +0000 by andy1398