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

JavaScript日历切换异常:12月无法跳转至次年1月

日历组件月份切换异常问题修复

问题描述

开发JavaScript日历组件时出现月份切换异常:处于2023年12月时,点击下月按钮无法切换到2024年1月,直接跳转到2024年12月;切换上月时也存在同类错误。

问题根源分析

  1. 参数赋值逻辑错误:createCalendar函数开头使用||运算符判断参数是否存在,但当传入的月份为0(对应1月)时,0会被判定为false,导致currentMonth被强制重置为当前系统月份,完全忽略了传入的参数值。
  2. 重复绑定点击事件:每次调用createCalendar都会为新生成的<button>元素绑定一次点击事件,多次切换后同一按钮会触发多次月份变更操作,加剧了跳转异常。

修复方案

1. 修正参数赋值逻辑

将currentYear和currentMonth的赋值逻辑改为判断参数是否为undefined,避免合法的0值被误判:

// 替换原createCalendar函数开头的赋值代码
const today = new Date();
currentYear = typeof year !== 'undefined' ? year : today.getFullYear();
currentMonth = typeof month !== 'undefined' ? month : today.getMonth();

2. 移除重复的事件绑定

将按钮点击事件的绑定逻辑移出createCalendar函数,放到DOMContentLoaded的顶层作用域,避免每次重建日历DOM时重复绑定:

// 在createCalendar函数外部添加事件委托(或直接绑定,但委托更稳定)
document.addEventListener('click', function(e) {
  if (e.target.matches('#prevMonth')) {
    currentMonth--;
    if (currentMonth < 0) {
      currentMonth = 11;
      currentYear--;
    }
    createCalendar(currentYear, currentMonth);
  } else if (e.target.matches('#nextMonth')) {
    currentMonth++;
    if (currentMonth > 11) {
      currentMonth = 0;
      currentYear++;
    }
    createCalendar(currentYear, currentMonth);
  }
});

同时删除createCalendar函数内部的prevMonthBtn和nextMonthBtn的事件绑定代码。

修复后的完整代码

document.addEventListener('DOMContentLoaded', function () {
    const calendar = document.getElementById("calendar");
    let currentYear, currentMonth;
    const monthNames = [
        "Styczeń", "Luty", "Marzec", "Kwiecień", "Maj", "Czerwiec",
        "Lipiec", "Sierpień", "Wrzesień", "Październik", "Listopad", "Grudzień"
    ];
    const dayNames = ["Pn", "Wt", "Śr", "Czw", "Pt", "Sob", "Nie"];

    // 全局事件委托处理月份切换
    document.addEventListener('click', function(e) {
        if (e.target.matches('#prevMonth')) {
            currentMonth--;
            if (currentMonth < 0) {
                currentMonth = 11;
                currentYear--;
            }
            createCalendar(currentYear, currentMonth);
        } else if (e.target.matches('#nextMonth')) {
            currentMonth++;
            if (currentMonth > 11) {
                currentMonth = 0;
                currentYear++;
            }
            createCalendar(currentYear, currentMonth);
        }
    });

    function createCalendar(year, month) {
        const today = new Date();
        // 修正参数赋值逻辑
        currentYear = typeof year !== 'undefined' ? year : today.getFullYear();
        currentMonth = typeof month !== 'undefined' ? month : today.getMonth();
        const currentDay = today.getDate();
        const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
        const firstDay = new Date(currentYear, currentMonth, 1).getDay() || 7;
        calendar.innerHTML = "";
        const monthName = monthNames[currentMonth];
        calendar.innerHTML += `<div class="d-flex title-month">  
            <button id="prevMonth" class="prevMonth"><<</button>
            <h2 class="nameOfMonth">${monthName} ${currentYear}</h2>
            <button id="nextMonth" class="nextMonth">>></button></div>
        `;
        const table = document.createElement("table");
        let day = 1;
        const headerRow = document.createElement("tr");
        dayNames.forEach(function (dayName) {
            const th = document.createElement("th");
            th.textContent = dayName;
            headerRow.appendChild(th);
        });
        table.appendChild(headerRow);
        for (let i = 0; i < 6; i++) {
            const row = document.createElement("tr");
            for (let j = 0; j < 7; j++) {
                if (i === 0 && j < firstDay - 1) {
                    const cell = document.createElement("td");
                    row.appendChild(cell);
                } else if (day <= daysInMonth) {
                    const cell = document.createElement("td");
                    cell.textContent = day;
                    if (day === currentDay && currentMonth === today.getMonth() && currentYear === today.getFullYear()) {
                        cell.classList.add("today");
                    } else if (new Date(currentYear, currentMonth, day) < today) {
                        cell.classList.add("past");
                    }
                    if (j === 5 || j === 6) {
                        cell.classList.add("weekend");
                    }
                    row.appendChild(cell);
                    day++;
                    if (!(new Date(currentYear, currentMonth, day - 1) <= today)) {
                        cell.addEventListener("click", (event) => {
                            if (!event.target.classList.contains("past")) {
                                const selectedDay = event.target.textContent;
                                openPopup(selectedDay, currentMonth + 1, currentYear);
                            }
                        });
                    }
                }
            }
            table.appendChild(row);
        }
        calendar.appendChild(table);
        
        function openPopup(day, month, year) {
            const popup = document.createElement("div");
            popup.classList.add("popup");
            popup.innerHTML = `
            <form id="bookingForm" method="post" action="/saveBooking">
                <input type="hidden" name="selected_date" id="selected_date" value="${year}-${month}-${day}">
                <span class="close-button" onclick="closePopup()">X</span>
                <h3>${day}.${month}.${year}</h3>
                <select name="category">
                    <option value="category1">Категорія 1</option>
                    <option value="category2">Категорія 2</option>
                    <option value="category3">Категорія 3</option>
                </select>
                <select name="time">
                    <option value="08:00">08:00</option>
                    <option value="09:00">09:00</option>
                    <option value="10:00">10:00</option>
                    <option value="11:00">11:00</option>
                    <option value="12:00">12:00</option>
                    <option value="13:00">13:00</option>
                    <option value="14:00">14:00</option>
                    <option value="15:00">15:00</option>
                    <option value="16:00">16:00</option>
                </select>
                <button type="submit" name="submit">Записатися</button>
            </form>
            `;
            document.body.appendChild(popup);
            const closeBtn = popup.querySelector(".close-button");
            closeBtn.addEventListener("click", function () {
                closePopup();
            });
        }

        function closePopup() {
            const popup = document.querySelector(".popup");
            if (popup) {
                popup.remove();
            }
        }
    }
    createCalendar();
});

内容的提问来源于stack exchange,提问作者Максим Каленюк

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:35:13