Skip to content

Commit 19abdd1

Browse files
raza-khan0108sumn2u
authored andcommitted
feat: add interactive calendar with event support
1 parent 9b4e52a commit 19abdd1

4 files changed

Lines changed: 371 additions & 0 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# 📅 Interactive Calendar
2+
3+
A lightweight, responsive calendar widget built with vanilla JavaScript. This project demonstrates DOM manipulation, Date object handling, and local state management without external libraries.
4+
5+
## 🚀 Features
6+
7+
- **Dynamic Rendering:** Automatically generates the correct grid for any month and year.
8+
- **Navigation:** Browse through past and future months.
9+
- **Current Date Highlighting:** visual indicator for today's date.
10+
- **Event Management (Bonus):** Click any date to add, view, or delete notes.
11+
- **Data Persistence:** Events are saved to the browser's `localStorage`, so they remain after refreshing the page.
12+
- **Responsive Design:** Built with CSS Grid to adapt to different screen sizes.
13+
14+
## 🛠️ Technologies Used
15+
16+
- **HTML5:** Semantic structure.
17+
- **CSS3:** Flexbox and Grid layout; CSS Variables for theming.
18+
- **JavaScript (ES6+):** Logic for date calculation and event handling.
19+
20+
## 📂 Project Structure
21+
22+
```text
23+
interactive-calendar/
24+
├── index.html # Main HTML structure
25+
├── style.css # Styling and Grid layout
26+
├── script.js # Calendar logic and Event handling
27+
└── README.md # Project documentation
28+
29+
##💡 How It Works
30+
1. Date Calculation
31+
The calendar grid is calculated using the native Date object:
32+
33+
Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
34+
35+
Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
36+
37+
2. Rendering the Grid
38+
We use a loop to generate <div> elements.
39+
40+
First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
41+
42+
Then, we generate the actual numbered days.
43+
44+
3. State Management
45+
Events are stored in a simple JSON object and saved to LocalStorage:
46+
47+
JavaScript
48+
// Data Structure Example
49+
{
50+
"2023-10-25": "Meeting with team",
51+
"2023-10-31": "Halloween Party"
52+
}
53+
54+
## 🏃‍♂️ How to Run
55+
Clone the repository.
56+
57+
Navigate to the interactive-calendar folder.
58+
59+
Open index.html in your browser.
60+
61+
# 🔮 Future Improvements
62+
Add drag-and-drop functionality for events.
63+
64+
Support for multiple events per day.
65+
66+
Add specific time slots for events.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Interactive Calendar</title>
7+
<link rel="stylesheet" href="style.css">
8+
</head>
9+
<body>
10+
11+
<div class="calendar-container">
12+
<header class="calendar-header">
13+
<div class="calendar-navigation">
14+
<span id="month-prev" class="nav-btn">&lt;</span>
15+
<h2 id="month-year"></h2>
16+
<span id="month-next" class="nav-btn">&gt;</span>
17+
</div>
18+
</header>
19+
20+
<div class="calendar-weekdays">
21+
<div>Sun</div><div>Mon</div><div>Tue</div><div>Wed</div><div>Thu</div><div>Fri</div><div>Sat</div>
22+
</div>
23+
24+
<div class="calendar-dates" id="calendar-dates"></div>
25+
</div>
26+
27+
<div id="event-modal" class="modal hidden">
28+
<div class="modal-content">
29+
<span class="close-btn">&times;</span>
30+
<h3 id="selected-date"></h3>
31+
<textarea id="event-input" placeholder="Add an event/note..."></textarea>
32+
<button id="save-event-btn">Save Event</button>
33+
<button id="delete-event-btn" class="secondary">Clear Event</button>
34+
</div>
35+
</div>
36+
37+
<script src="script.js"></script>
38+
</body>
39+
</html>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
const monthYearElement = document.getElementById('month-year');
2+
const datesElement = document.getElementById('calendar-dates');
3+
const prevBtn = document.getElementById('month-prev');
4+
const nextBtn = document.getElementById('month-next');
5+
6+
// Modal Elements
7+
const modal = document.getElementById('event-modal');
8+
const closeModalBtn = document.querySelector('.close-btn');
9+
const selectedDateTitle = document.getElementById('selected-date');
10+
const eventInput = document.getElementById('event-input');
11+
const saveEventBtn = document.getElementById('save-event-btn');
12+
const deleteEventBtn = document.getElementById('delete-event-btn');
13+
14+
let currentDate = new Date();
15+
let clickedDate = null;
16+
let events = JSON.parse(localStorage.getItem('events')) || {};
17+
18+
const months = [
19+
"January", "February", "March", "April", "May", "June",
20+
"July", "August", "September", "October", "November", "December"
21+
];
22+
23+
// --- Core Functions ---
24+
25+
function renderCalendar() {
26+
const year = currentDate.getFullYear();
27+
const month = currentDate.getMonth();
28+
29+
monthYearElement.innerText = `${months[month]} ${year}`;
30+
datesElement.innerHTML = '';
31+
32+
// First day of the month (0 = Sunday, 1 = Monday, etc.)
33+
const firstDay = new Date(year, month, 1).getDay();
34+
// Total days in the current month
35+
const totalDays = new Date(year, month + 1, 0).getDate();
36+
37+
// Add empty divs for days before the first day of the month
38+
for (let i = 0; i < firstDay; i++) {
39+
const emptyDiv = document.createElement('div');
40+
datesElement.appendChild(emptyDiv);
41+
}
42+
43+
// Render days
44+
for (let day = 1; day <= totalDays; day++) {
45+
const dayDiv = document.createElement('div');
46+
dayDiv.classList.add('day');
47+
dayDiv.innerText = day;
48+
49+
// Check if this day is "Today"
50+
const today = new Date();
51+
if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
52+
dayDiv.classList.add('current-date');
53+
}
54+
55+
// Check for Events
56+
const dateString = `${year}-${month + 1}-${day}`;
57+
if (events[dateString]) {
58+
dayDiv.classList.add('has-event');
59+
dayDiv.title = events[dateString]; // Tooltip
60+
}
61+
62+
// Add Click Event for Modal
63+
dayDiv.addEventListener('click', () => openModal(dateString));
64+
65+
datesElement.appendChild(dayDiv);
66+
}
67+
}
68+
69+
function openModal(dateStr) {
70+
clickedDate = dateStr;
71+
selectedDateTitle.innerText = `Event for: ${dateStr}`;
72+
eventInput.value = events[dateStr] || '';
73+
modal.classList.remove('hidden');
74+
}
75+
76+
function closeModal() {
77+
modal.classList.add('hidden');
78+
clickedDate = null;
79+
}
80+
81+
// --- Event Listeners ---
82+
83+
prevBtn.addEventListener('click', () => {
84+
currentDate.setMonth(currentDate.getMonth() - 1);
85+
renderCalendar();
86+
});
87+
88+
nextBtn.addEventListener('click', () => {
89+
currentDate.setMonth(currentDate.getMonth() + 1);
90+
renderCalendar();
91+
});
92+
93+
closeModalBtn.addEventListener('click', closeModal);
94+
95+
saveEventBtn.addEventListener('click', () => {
96+
if (eventInput.value.trim()) {
97+
events[clickedDate] = eventInput.value;
98+
localStorage.setItem('events', JSON.stringify(events));
99+
}
100+
closeModal();
101+
renderCalendar();
102+
});
103+
104+
deleteEventBtn.addEventListener('click', () => {
105+
if (events[clickedDate]) {
106+
delete events[clickedDate];
107+
localStorage.setItem('events', JSON.stringify(events));
108+
}
109+
closeModal();
110+
renderCalendar();
111+
});
112+
113+
// Close modal if clicking outside
114+
window.addEventListener('click', (e) => {
115+
if (e.target === modal) {
116+
closeModal();
117+
}
118+
});
119+
120+
// Initial Render
121+
renderCalendar();
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
:root {
2+
--primary-color: #4a90e2;
3+
--bg-color: #f4f4f9;
4+
--text-color: #333;
5+
--highlight-color: #e0f7fa;
6+
}
7+
8+
body {
9+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10+
background-color: var(--bg-color);
11+
display: flex;
12+
justify-content: center;
13+
align-items: center;
14+
min-height: 100vh;
15+
margin: 0;
16+
}
17+
18+
.calendar-container {
19+
background: white;
20+
width: 90%;
21+
max-width: 400px;
22+
border-radius: 10px;
23+
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
24+
overflow: hidden;
25+
padding: 20px;
26+
}
27+
28+
.calendar-header {
29+
display: flex;
30+
justify-content: center;
31+
align-items: center;
32+
margin-bottom: 20px;
33+
}
34+
35+
.calendar-navigation {
36+
display: flex;
37+
align-items: center;
38+
justify-content: space-between;
39+
width: 100%;
40+
}
41+
42+
.nav-btn {
43+
cursor: pointer;
44+
font-size: 1.5rem;
45+
-webkit-user-select: none;
46+
user-select: none;
47+
padding: 0 10px;
48+
}
49+
50+
.nav-btn:hover {
51+
color: var(--primary-color);
52+
}
53+
54+
/* Grid System */
55+
.calendar-weekdays, .calendar-dates {
56+
display: grid;
57+
grid-template-columns: repeat(7, 1fr);
58+
text-align: center;
59+
}
60+
61+
.calendar-weekdays div {
62+
font-weight: bold;
63+
color: #888;
64+
margin-bottom: 10px;
65+
}
66+
67+
.day {
68+
padding: 10px;
69+
cursor: pointer;
70+
border-radius: 5px;
71+
transition: background 0.2s;
72+
position: relative;
73+
}
74+
75+
.day:hover {
76+
background-color: #f0f0f0;
77+
}
78+
79+
.day.current-date {
80+
background-color: var(--primary-color);
81+
color: white;
82+
font-weight: bold;
83+
}
84+
85+
.day.has-event::after {
86+
content: '';
87+
position: absolute;
88+
bottom: 5px;
89+
left: 50%;
90+
transform: translateX(-50%);
91+
width: 5px;
92+
height: 5px;
93+
background-color: red;
94+
border-radius: 50%;
95+
}
96+
97+
.day.inactive {
98+
color: #ccc;
99+
pointer-events: none;
100+
}
101+
102+
/* Modal Styles */
103+
.modal {
104+
position: fixed;
105+
top: 0; left: 0; width: 100%; height: 100%;
106+
background: rgba(0,0,0,0.5);
107+
display: flex;
108+
justify-content: center;
109+
align-items: center;
110+
}
111+
112+
.hidden { display: none; }
113+
114+
.modal-content {
115+
background: white;
116+
padding: 20px;
117+
border-radius: 8px;
118+
width: 300px;
119+
position: relative;
120+
}
121+
122+
.close-btn {
123+
position: absolute;
124+
top: 10px; right: 15px;
125+
cursor: pointer;
126+
font-size: 1.2rem;
127+
}
128+
129+
textarea {
130+
width: 100%;
131+
height: 60px;
132+
margin: 10px 0;
133+
padding: 5px;
134+
}
135+
136+
button {
137+
background: var(--primary-color);
138+
color: white;
139+
border: none;
140+
padding: 8px 15px;
141+
cursor: pointer;
142+
border-radius: 4px;
143+
}
144+
145+
button.secondary { background: #ccc; }

0 commit comments

Comments
 (0)