You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带事件展示的日历开发:仅二月渲染异常求助

解决二月日历渲染异常的问题

我仔细看了你的代码,发现了两个导致二月渲染异常的关键问题,下面一步步帮你解决:

问题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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:07:48