带事件展示的日历开发:仅二月渲染异常求助
解决二月日历渲染异常的问题
我仔细看了你的代码,发现了两个导致二月渲染异常的关键问题,下面一步步帮你解决:
问题1:事件键的类型不匹配
在你的listEvents对象中,二月的事件键是字符串类型的"22",但在循环生成日期时,你用的是数字类型的i来判断hasOwnProperty(i)。JavaScript中对象的键会自动转为字符串,但用数字去调用hasOwnProperty时不会自动转换类型,所以listEvents["February"].hasOwnProperty(22)会返回false,导致二月的事件无法被正确识别,日期显示不符合预期。
修复方法:
将判断条件里的数字i转为字符串,或者统一把listEvents中的键处理为字符串匹配,这里我们修改判断逻辑更简单:
把原条件
if (listEvents.hasOwnProperty(months[date.getMonth()]) && listEvents[months[date.getMonth()]].hasOwnProperty(i))
改成
const currentMonthEvents = listEvents[months[date.getMonth()]]; if (currentMonthEvents && currentMonthEvents.hasOwnProperty(String(i)))
问题2:DOM更新位置错误
你把monthDays.innerHTML = days;放在了nextDays的for循环内部,这会导致每添加一个下月日期就更新一次DOM,不仅效率低,还可能导致渲染不完整。正确的做法是把这行代码放在所有循环(上月日期、当月日期、下月日期)的外面,确保所有日期元素都拼接完成后再一次性更新DOM。
修复方法:
把monthDays.innerHTML = days;从nextDays的循环里移出来,放在三个循环都结束之后。
完整修正后的JavaScript代码
const date = new Date(); const renderCalendar = () => { date.setDate(1); const listEvents = { "January": { "16": ["Birthday", "Seema Sinha"], "25": ["Anniversary", "Seema Sinha"] }, "February": { "22": ["Birthday", "Prateek Sinha"] }, "March": { "14": ["Birthday", "Ajay Kumar Sinha"] }, "April": { "30": ["Birthday", "Sweta Kumari"] }, "May": { "17": ["Birthday", "Pradeep Kumar"] }, "June": { "10": ["Birthday", "Abha Sinha"], "18": ["Anniversary", "Lalita Sinha"], "27": ["Anniversary", "Jyoti Sinha"] }, "August": { "10": ["Birthday", "Sachin Sinha"] }, "September": { "26": ["Birthday", "Runnu Sinha"], "28": ["Birthday", "Prabhat Kumar", "Pratyaksha Sinha"] }, "October": { "2": ["Birthday", "Pranay Sinha"] }, "November": { "4": ["Birthday", "Prajjwal Sinha"], "22": ["Anniversary", "Abha Sinha"], "23": ["Birthday", "Lalita Sinha"] }, "December": { "10": ["Birthday", "Jyoti Sinha"], "1": ["Birthday", "Pranav Sinha"], "25": ["Birthday", "Bhavya Sinha"], "26": ["Birthday", "Pravin Kumar"] } }; const monthDays = document.querySelector(".days"); const lastDay = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDate(); const prevLastDay = new Date( date.getFullYear(), date.getMonth(), 0 ).getDate(); const firstDayIndex = date.getDay(); const lastDayIndex = new Date( date.getFullYear(), date.getMonth() + 1, 0 ).getDay(); const nextDays = 7 - lastDayIndex - 1; const months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ]; document.querySelector(".date h1").innerHTML = months[date.getMonth()]; document.querySelector(".date p").innerHTML = new Date().toDateString(); let days = ""; // 生成上月剩余日期 for (let x = firstDayIndex; x > 0; x--) { days += `<div class="prev-date">${prevLastDay - x +1}</div>`; } // 生成当月日期 const currentMonthName = months[date.getMonth()]; const currentMonthEvents = listEvents[currentMonthName]; for (let i = 1; i <= lastDay; i++) { const dayStr = String(i); let dayContent = `${i}`; let dayClass = "normal-dates"; // 判断是否是今天 if (i === new Date().getDate() && date.getMonth() === new Date().getMonth()) { dayClass = "today"; } // 判断是否有事件 if (currentMonthEvents && currentMonthEvents[dayStr]) { const [eventType] = currentMonthEvents[dayStr]; if (eventType === "Birthday") { dayContent = `<i class="fas fa-birthday-cake" style="color: #CBAB1A"></i>`; } else if (eventType === "Anniversary") { dayContent = `<i class="fas fa-heart" style="color:red;"></i>`; } } days += `<div class="${dayClass}">${dayContent}</div>`; } // 生成下月日期 for (let j = 1; j <= nextDays; j++) { days += `<div class="next-date">${j}</div>`; } // 统一更新DOM monthDays.innerHTML = days; // 日期点击事件 document.querySelectorAll(".normal-dates").forEach(item => { item.addEventListener("click", event => { const targetDate = new Date(); targetDate.setDate(parseInt(event.target.textContent)); targetDate.setMonth(date.getMonth()); const targetMonthName = months[targetDate.getMonth()]; const targetMonthEvents = listEvents[targetMonthName]; if (targetMonthEvents) { const dayStr = String(targetDate.getDate()); if (targetMonthEvents[dayStr]) { console.log(targetMonthEvents[dayStr]); } } console.log(targetDate.toDateString()); }) }); }; document.querySelector(".prev").addEventListener("click", () => { date.setMonth(date.getMonth() - 1); renderCalendar(); }); document.querySelector(".next").addEventListener("click", () => { date.setMonth(date.getMonth() + 1); renderCalendar(); }); renderCalendar();
额外优化建议
- 我把事件判断的逻辑做了简化,避免重复的条件判断,让代码更易读;
- 点击事件里也把日期转为字符串来匹配事件键,避免同样的类型问题;
- 提取了
currentMonthName和currentMonthEvents变量,减少重复的属性访问,提升代码效率。
现在二月应该能正常显示事件,所有月份的渲染也会更稳定啦!
内容的提问来源于stack exchange,提问作者Pranav Sinha
相关产品推荐
相关产品推荐

