Skip to content

Commit 878578a

Browse files
raza-khan0108sumn2u
authored andcommitted
fix: address code review feedback (accessibility, renaming, and logic fixes)
1 parent 19abdd1 commit 878578a

4 files changed

Lines changed: 109 additions & 67 deletions

File tree

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,90 @@
11
# 📅 Interactive Calendar
22

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.
3+
A lightweight, responsive calendar widget built with **vanilla JavaScript**.
4+
This project demonstrates **DOM manipulation**, **Date object handling**, and **local state management** without external libraries.
5+
6+
---
47

58
## 🚀 Features
69

710
- **Dynamic Rendering:** Automatically generates the correct grid for any month and year.
811
- **Navigation:** Browse through past and future months.
9-
- **Current Date Highlighting:** visual indicator for today's date.
12+
- **Current Date Highlighting:** Visual indicator for today's date.
1013
- **Event Management (Bonus):** Click any date to add, view, or delete notes.
1114
- **Data Persistence:** Events are saved to the browser's `localStorage`, so they remain after refreshing the page.
1215
- **Responsive Design:** Built with CSS Grid to adapt to different screen sizes.
1316

17+
---
18+
1419
## 🛠️ Technologies Used
1520

16-
- **HTML5:** Semantic structure.
17-
- **CSS3:** Flexbox and Grid layout; CSS Variables for theming.
18-
- **JavaScript (ES6+):** Logic for date calculation and event handling.
21+
- **HTML5:** Semantic structure
22+
- **CSS3:** Flexbox and Grid layout, CSS Variables for theming
23+
- **JavaScript (ES6+):** Date calculation and event handling logic
24+
25+
---
1926

2027
## 📂 Project Structure
2128

2229
```text
2330
interactive-calendar/
2431
├── index.html # Main HTML structure
2532
├── style.css # Styling and Grid layout
26-
├── script.js # Calendar logic and Event handling
33+
├── script.js # Calendar logic and event handling
2734
└── README.md # Project documentation
35+
```
36+
37+
---
38+
39+
## 💡 How It Works
2840

29-
##💡 How It Works
30-
1. Date Calculation
31-
The calendar grid is calculated using the native Date object:
41+
### 1. Date Calculation
3242

33-
Start Day: new Date(year, month, 1).getDay() determines which day of the week the month starts on (0=Sunday, 1=Monday).
43+
The calendar grid is calculated using the native `Date` object:
3444

35-
Total Days: new Date(year, month + 1, 0).getDate() retrieves the exact number of days in the current month.
45+
- **Start Day:**
46+
`new Date(year, month, 1).getDay()`
47+
Determines which day of the week the month starts on
48+
`(0 = Sunday, 1 = Monday, ...)`
3649

37-
2. Rendering the Grid
38-
We use a loop to generate <div> elements.
50+
- **Total Days:**
51+
`new Date(year, month + 1, 0).getDate()`
52+
Retrieves the exact number of days in the current month
3953

40-
First, we insert empty placeholder divs to align the 1st of the month with the correct weekday column.
54+
---
4155

42-
Then, we generate the actual numbered days.
56+
### 2. Rendering the Grid
4357

44-
3. State Management
45-
Events are stored in a simple JSON object and saved to LocalStorage:
58+
- A loop generates `<div>` elements for the calendar.
59+
- Empty placeholder divs are inserted to align the first day of the month correctly.
60+
- Numbered day cells are then rendered dynamically.
4661

47-
JavaScript
62+
---
63+
64+
### 3. State Management
65+
66+
Events are stored in a simple JSON object and persisted using `localStorage`.
67+
68+
```javascript
4869
// Data Structure Example
4970
{
5071
"2023-10-25": "Meeting with team",
5172
"2023-10-31": "Halloween Party"
5273
}
74+
```
5375

54-
## 🏃‍♂️ How to Run
55-
Clone the repository.
76+
---
5677

57-
Navigate to the interactive-calendar folder.
78+
## 🏃‍♂️ How to Run
5879

59-
Open index.html in your browser.
80+
1. Clone the repository
81+
2. Navigate to the `interactive-calendar` folder
82+
3. Open `index.html` in your browser
6083

61-
# 🔮 Future Improvements
62-
Add drag-and-drop functionality for events.
84+
---
6385

64-
Support for multiple events per day.
86+
## 🔮 Future Improvements
6587

66-
Add specific time slots for events.
88+
- Add drag-and-drop functionality for events
89+
- Support multiple events per day
90+
- Add specific time slots for events

examples/interactive-calendar/index.html

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Interactive Calendar</title>
7-
<link rel="stylesheet" href="style.css">
8-
</head>
7+
<link rel="stylesheet" href="styles.css"> </head>
98
<body>
109

1110
<div class="calendar-container">
1211
<header class="calendar-header">
1312
<div class="calendar-navigation">
14-
<span id="month-prev" class="nav-btn">&lt;</span>
13+
<span id="month-prev" class="nav-btn" role="button" tabindex="0" aria-label="Previous month">&lt;</span>
1514
<h2 id="month-year"></h2>
16-
<span id="month-next" class="nav-btn">&gt;</span>
15+
<span id="month-next" class="nav-btn" role="button" tabindex="0" aria-label="Next month">&gt;</span>
1716
</div>
1817
</header>
1918

@@ -24,9 +23,9 @@ <h2 id="month-year"></h2>
2423
<div class="calendar-dates" id="calendar-dates"></div>
2524
</div>
2625

27-
<div id="event-modal" class="modal hidden">
26+
<div id="event-modal" class="modal hidden" role="dialog" aria-modal="true" aria-labelledby="selected-date">
2827
<div class="modal-content">
29-
<span class="close-btn">&times;</span>
28+
<span class="close-btn" role="button" tabindex="0" aria-label="Close">&times;</span>
3029
<h3 id="selected-date"></h3>
3130
<textarea id="event-input" placeholder="Add an event/note..."></textarea>
3231
<button id="save-event-btn">Save Event</button>

examples/interactive-calendar/script.js

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const datesElement = document.getElementById('calendar-dates');
33
const prevBtn = document.getElementById('month-prev');
44
const nextBtn = document.getElementById('month-next');
55

6-
// Modal Elements
76
const modal = document.getElementById('event-modal');
87
const closeModalBtn = document.querySelector('.close-btn');
98
const selectedDateTitle = document.getElementById('selected-date');
@@ -20,7 +19,8 @@ const months = [
2019
"July", "August", "September", "October", "November", "December"
2120
];
2221

23-
// --- Core Functions ---
22+
// Helper: Zero-pad numbers (Fixed: Date formatting consistency)
23+
const pad = (n) => (n < 10 ? '0' + n : n);
2424

2525
function renderCalendar() {
2626
const year = currentDate.getFullYear();
@@ -29,74 +29,72 @@ function renderCalendar() {
2929
monthYearElement.innerText = `${months[month]} ${year}`;
3030
datesElement.innerHTML = '';
3131

32-
// First day of the month (0 = Sunday, 1 = Monday, etc.)
3332
const firstDay = new Date(year, month, 1).getDay();
34-
// Total days in the current month
3533
const totalDays = new Date(year, month + 1, 0).getDate();
3634

37-
// Add empty divs for days before the first day of the month
3835
for (let i = 0; i < firstDay; i++) {
3936
const emptyDiv = document.createElement('div');
4037
datesElement.appendChild(emptyDiv);
4138
}
4239

43-
// Render days
4440
for (let day = 1; day <= totalDays; day++) {
4541
const dayDiv = document.createElement('div');
4642
dayDiv.classList.add('day');
4743
dayDiv.innerText = day;
4844

49-
// Check if this day is "Today"
5045
const today = new Date();
5146
if (day === today.getDate() && month === today.getMonth() && year === today.getFullYear()) {
5247
dayDiv.classList.add('current-date');
5348
}
5449

55-
// Check for Events
56-
const dateString = `${year}-${month + 1}-${day}`;
50+
// Fixed: Use padded date format YYYY-MM-DD
51+
const dateString = `${year}-${pad(month + 1)}-${pad(day)}`;
52+
53+
// Store date in data attribute for Event Delegation
54+
dayDiv.dataset.date = dateString;
55+
5756
if (events[dateString]) {
5857
dayDiv.classList.add('has-event');
59-
dayDiv.title = events[dateString]; // Tooltip
58+
dayDiv.title = events[dateString];
6059
}
6160

62-
// Add Click Event for Modal
63-
dayDiv.addEventListener('click', () => openModal(dateString));
64-
61+
// Removed individual event listeners here (Performance fix)
6562
datesElement.appendChild(dayDiv);
6663
}
6764
}
6865

66+
// Fixed: Event Delegation (One listener for all days)
67+
datesElement.addEventListener('click', (e) => {
68+
const target = e.target.closest('.day');
69+
if (target && target.dataset.date) {
70+
openModal(target.dataset.date);
71+
}
72+
});
73+
6974
function openModal(dateStr) {
7075
clickedDate = dateStr;
7176
selectedDateTitle.innerText = `Event for: ${dateStr}`;
7277
eventInput.value = events[dateStr] || '';
7378
modal.classList.remove('hidden');
79+
eventInput.focus();
7480
}
7581

7682
function closeModal() {
7783
modal.classList.add('hidden');
7884
clickedDate = null;
7985
}
8086

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-
87+
// Fixed: Save logic handles whitespace and deletion
9588
saveEventBtn.addEventListener('click', () => {
96-
if (eventInput.value.trim()) {
97-
events[clickedDate] = eventInput.value;
98-
localStorage.setItem('events', JSON.stringify(events));
89+
const eventText = eventInput.value.trim();
90+
91+
if (eventText) {
92+
events[clickedDate] = eventText;
93+
} else {
94+
delete events[clickedDate]; // Remove event if input is cleared
9995
}
96+
97+
localStorage.setItem('events', JSON.stringify(events));
10098
closeModal();
10199
renderCalendar();
102100
});
@@ -110,12 +108,31 @@ deleteEventBtn.addEventListener('click', () => {
110108
renderCalendar();
111109
});
112110

113-
// Close modal if clicking outside
111+
// Navigation & Close
112+
prevBtn.addEventListener('click', () => {
113+
currentDate.setMonth(currentDate.getMonth() - 1);
114+
renderCalendar();
115+
});
116+
117+
nextBtn.addEventListener('click', () => {
118+
currentDate.setMonth(currentDate.getMonth() + 1);
119+
renderCalendar();
120+
});
121+
122+
closeModalBtn.addEventListener('click', closeModal);
123+
114124
window.addEventListener('click', (e) => {
115-
if (e.target === modal) {
116-
closeModal();
117-
}
125+
if (e.target === modal) closeModal();
126+
});
127+
128+
// Fixed: Keyboard Accessibility for Nav Buttons
129+
[prevBtn, nextBtn, closeModalBtn].forEach(btn => {
130+
btn.addEventListener('keydown', (e) => {
131+
if (e.key === 'Enter' || e.key === ' ') {
132+
e.preventDefault();
133+
btn.click();
134+
}
135+
});
118136
});
119137

120-
// Initial Render
121138
renderCalendar();

examples/interactive-calendar/style.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ textarea {
131131
height: 60px;
132132
margin: 10px 0;
133133
padding: 5px;
134+
box-sizing: border-box; /* Fixed: prevents width overflow */
135+
resize: vertical; /* Optional: improves UX */
134136
}
135137

136138
button {

0 commit comments

Comments
 (0)