Custom Calendar Component with Vanilla JavaScript
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Calendar</title>
<link rel="stylesheet" href="../css/calendar.css">
</head>
<body>
<div class="calendar-container">
<div class="calendar-header">
<input type="date" class="date-input">
</div>
<div class="calendar-body">
<button class="nav-button prev">
<
</button>
<div class="calendar-main">
<table class="calendar-table">
</table>
</div>
<button class="nav-button next">
>
</button>
</div>
</div>
</body>
</html>
<script src="../js/calendar.js"></script>
CSS Styling
* {
margin: 0;
padding: 0;
}
ul, li, ol {
list-style-type: none;
}
button {
cursor: pointer;
border: none;
background: rgba(255, 255, 255, 1);
}
.calendar-container {
width: 500px;
height: 430px;
margin: 100px auto;
border: 1px #ccc solid;
}
.calendar-header {
width: 100%;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px #ccc solid;
}
.calendar-body {
width: 100%;
height: 400px;
display: flex;
}
.nav-button {
width: 50px;
height: 100%;
line-height: 400px;
text-align: center;
font-size: 30px;
border-bottom: 1px #ccc solid;
}
.prev {
border-right: 1px #ccc solid;
}
.next {
border-left: 1px #ccc solid;
}
.calendar-main {
width: 400px;
height: 400px;
border-left: 1px #ccc solid;
border-right: 1px #ccc solid;
}
.calendar-table {
width: 100%;
height: 100%;
}
th, td {
text-align: center;
width: 50px;
height: 50px;
border: 1px #ccc solid;
}
.selected-date {
background: #04beff;
color: #fff;
}
.empty-date {
background: rgba(0, 0, 0, 0.1);
color: #ccc;
}
JavaScript Functionality
// DOM elements
const calendarTable = document.querySelector('.calendar-table');
const dateInput = document.querySelector('.date-input');
const prevButton = document.querySelector('.prev');
const nextButton = document.querySelector('.next');
let dateCells;
// Current date state
let currentDate = new Date();
let currentYear = currentDate.getFullYear();
let currentMonth = Number(currentDate.getMonth()) + 1;
let currentDay = currentDate.getDate();
// Initialize date input field
function initializeDateInput() {
const formattedMonth = currentMonth < 10 ? `0${currentMonth}` : currentMonth;
const formattedDay = currentDay < 10 ? `0${currentDay}` : currentDay;
dateInput.value = `${currentYear}-${formattedMonth}-${formattedDay}`;
}
// Get number of days in a month
function getDaysInMonth(year, month) {
const date = new Date(year, month - 1, 1);
date.setDate(date.getDate() - 1);
return date.getDate();
}
// Get last days of previous month
function getPreviousMonthDays(count) {
const prevMonthDate = new Date(currentYear, currentMonth - 1, 1);
const days = [];
for (let i = 1; i <= count; i++) {
const date = new Date(prevMonthDate.getFullYear(), prevMonthDate.getMonth(), prevMonthDate.getDate() - i);
days.push(date.getDate());
}
return days.reverse();
}
// Get first days of next month
function getNextMonthDays(count) {
const nextMonthDate = new Date(currentYear, currentMonth, 1);
const days = [];
for (let i = 1; i <= count; i++) {
const date = new Date(nextMonthDate.getFullYear(), nextMonthDate.getMonth(), i);
days.push(date.getDate());
}
return days;
}
// Array to store calendar data
let calendarData = [];
// Build calendar data array
function buildCalendarData(prevDaysCount, currentMonthDays) {
calendarData = [];
const prevMonthDays = getPreviousMonthDays(prevDaysCount);
const nextMonthDays = getNextMonthDays(42 - prevDaysCount - currentMonthDays);
calendarData.push(...prevMonthDays);
calendarData.push(...Array.from({length: currentMonthDays}, (_, i) => i + 1));
calendarData.push(...nextMonthDays);
}
// Calendar header HTML
const tableHeader = `
<tr>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
<th>Sun</th>
</tr>
`;
// Render calendar
function renderCalendar() {
// Get first day of month and number of days
const firstDay = new Date(currentYear, currentMonth - 1, 1);
const dayOfWeek = firstDay.getDay() || 7;
const daysInMonth = getDaysInMonth(currentYear, currentMonth);
// Build calendar data
buildCalendarData(dayOfWeek - 1, daysInMonth);
// Generate calendar rows
const tableRows = [tableHeader];
for (let i = 0; i < calendarData.length; i += 7) {
const rowCells = Array.from({length: 7}, (_, j) =>
`<td class="date-cell">${calendarData[i + j]}</td>`
).join('');
tableRows.push(`<tr>${rowCells}</tr>`);
}
// Update table content
calendarTable.innerHTML = tableRows.join('');
// Apply styling to date cells
dateCells = document.querySelectorAll('.date-cell');
dateCells.forEach((cell, index) => {
if (cell.textContent == currentDay && index >= dayOfWeek - 1 && index < daysInMonth + dayOfWeek - 1) {
cell.className = 'date-cell selected-date';
} else if (index < dayOfWeek - 1 || index > daysInMonth + dayOfWeek - 2) {
cell.className = 'date-cell empty-date';
}
});
}
// Reset calendar data
function resetCalendarData() {
calendarData = [];
}
// Initialize date input
initializeDateInput();
// Initial calendar render
renderCalendar();
// Handle date input change
dateInput.addEventListener('change', function() {
const [year, month, day] = dateInput.value.split('-').map(Number);
currentYear = year;
currentMonth = month;
currentDay = day;
resetCalendarData();
renderCalendar();
});
// Handle date cell click
calendarTable.addEventListener('click', function(e) {
if (e.target.classList.contains('date-cell')) {
const clickedDay = Number(e.target.textContent);
// Handle previous month navigation
if (e.target.classList.contains('empty-date') && clickedDay > 20) {
currentMonth--;
if (currentMonth <= 0) {
currentMonth = 12;
currentYear--;
}
}
// Handle next month navigation
if (e.target.classList.contains('empty-date') && clickedDay < 10) {
currentMonth++;
if (currentMonth > 12) {
currentMonth = 1;
currentYear++;
}
}
currentDay = clickedDay;
resetCalendarData();
renderCalendar();
initializeDateInput();
}
});
// Handle previous month button click
prevButton.addEventListener('click', function() {
currentMonth--;
if (currentMonth <= 0) {
currentMonth = 12;
currentYear--;
}
currentDay = 1;
resetCalendarData();
renderCalendar();
initializeDateInput();
});
// Handle next month button click
nextButton.addEventListener('click', function() {
currentMonth++;
if (currentMonth > 12) {
currentMonth = 1;
currentYear++;
}
currentDay = 1;
resetCalendarData();
renderCalendar();
initializeDateInput();
});