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

预订网站For循环失效:日期无法序列化,AJAX请求疑存问题

预订网站日历生成异常问题

我在开发一个预订网站时遇到了问题:For循环存在异常或逻辑错误,导致日历生成结果不符合预期(附旧版本预期效果截图与新版本实际输出截图)。项目采用PHP开发,其中check_date.php功能正常,问题疑似出在AJAX请求环节。

相关JavaScript代码如下:

// JavaScript code for generating the calendar and handling month navigation
const calendarContainer = document.getElementById("calendar");
const prevMonthBtn = document.getElementById("prevMonth");
const nextMonthBtn = document.getElementById("nextMonth");
const currentMonthYear = document.getElementById("currentMonthYear");

const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = [
  "January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"
];

const currentDate = new Date();
let currentMonth = currentDate.getMonth();
let currentYear = currentDate.getFullYear();

function generateCalendar() {
  const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
  const firstDayIndex = new Date(currentYear, currentMonth, 1).getDay();

  calendarContainer.innerHTML = "";

  // Display days of the week at the top of the calendar
  for (const dayOfWeek of daysOfWeek) {
    const dayOfWeekDiv = document.createElement("div");
    dayOfWeekDiv.classList.add("day");
    dayOfWeekDiv.textContent = dayOfWeek;
    calendarContainer.appendChild(dayOfWeekDiv);
  }

  for (let i = 0; i < firstDayIndex; i++) {
    const dayDiv = document.createElement("div");
    dayDiv.classList.add("day");
    calendarContainer.appendChild(dayDiv);
  }

  for (let i = 1; i <= daysInMonth; i++) {
    const dayDiv = document.createElement("a");
    dayDiv.href = "entry.php";
    let __ryear = currentYear.toString();
    let __rmonth = currentMonth.toString();
    let daI = i.toString();
    let result1 = __ryear.concat(__rmonth);
    let result2 = result1.concat(daI);
    // let ruiue = result2;
    // let date6969 = i;

    // AJAX request to check if the date exists in the database
    let xhr = new XMLHttpRequest();
    xhr.open("GET", "http://localhost/irctclogin/check_date.php?date=" + result2, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4 && xhr.status == 200) {
        let response = xhr.responseText;
        if (response == "exists") {

          // Date exists in the database, continue the program
          let bookStatus = "booked";
          dayDiv.classList.add("day", result2, bookStatus);
          dayDiv.textContent = i;
          calendarContainer.appendChild(dayDiv);
          // dayDiv.value = i;

        }
          else {
            let bookStatus = "available";
            dayDiv.classList.add("day", result2, bookStatus);
            dayDiv.textContent = i;
            calendarContainer.appendChild(dayDiv);
            // dayDiv.value = i;
            // console.log(i);
          }
        }
      }
      xhr.send();
    }

    currentMonthYear.textContent = `${months[currentMonth]} ${currentYear}`;
    // console.log(`${months[currentMonth]} ${currentYear}`);
  }

  prevMonthBtn.addEventListener("click", () => {
    if (currentMonth === 0) {
      currentMonth = 11;
      currentYear--;
    } else {
      currentMonth--;
    }
    generateCalendar();
  });

  nextMonthBtn.addEventListener("click", () => {
    if (currentMonth === 11) {
      currentMonth = 0;
      currentYear++;
    } else {
      currentMonth++;
    }
    generateCalendar();
  });

  generateCalendar();

问题分析

  1. 异步请求的变量绑定问题:循环中创建的AJAX请求是异步执行的,当回调触发时,循环已经执行完毕,dayDiv和i都会指向循环最后一次的变量值,导致所有日期节点都被错误地赋值为最后一天的状态。
  2. 日期格式错误:月份是0基数字(0代表1月),直接转字符串后,1-9月会是单字符(如"1"),拼接后的日期格式不符合常规(比如2024年1月5日会变成"202415"),后端check_date.php可能无法正确解析该日期,导致状态判断错误。
  3. 重复绑定事件监听:每次调用generateCalendar()都会给前后月份按钮添加新的点击事件,多次切换月份后,点击按钮会触发多次generateCalendar(),导致日历渲染混乱。

修复方案与代码

关键修改点

  • 使用立即执行函数绑定当前循环的变量,确保每个AJAX回调对应正确的日期节点和日期值;
  • 修复日期格式,月份(+1后)和日期不足两位时补0,生成标准的YYYYMMDD格式;
  • 将按钮点击事件移到generateCalendar()外部,只绑定一次,避免重复触发;
  • 先将日期节点添加到容器,再通过AJAX更新状态,保证日历结构先渲染完整。

修复后的完整代码:

// JavaScript code for generating the calendar and handling month navigation
const calendarContainer = document.getElementById("calendar");
const prevMonthBtn = document.getElementById("prevMonth");
const nextMonthBtn = document.getElementById("nextMonth");
const currentMonthYear = document.getElementById("currentMonthYear");

const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const months = [
  "January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"
];

const currentDate = new Date();
let currentMonth = currentDate.getMonth();
let currentYear = currentDate.getFullYear();

function generateCalendar() {
  const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
  const firstDayIndex = new Date(currentYear, currentMonth, 1).getDay();

  calendarContainer.innerHTML = "";

  // 渲染星期标题
  for (const dayOfWeek of daysOfWeek) {
    const dayOfWeekDiv = document.createElement("div");
    dayOfWeekDiv.classList.add("day");
    dayOfWeekDiv.textContent = dayOfWeek;
    calendarContainer.appendChild(dayOfWeekDiv);
  }

  // 填充当月第一天之前的空白格子
  for (let i = 0; i < firstDayIndex; i++) {
    const dayDiv = document.createElement("div");
    dayDiv.classList.add("day");
    calendarContainer.appendChild(dayDiv);
  }

  // 渲染当月每一天
  for (let i = 1; i <= daysInMonth; i++) {
    // 生成标准YYYYMMDD格式的日期字符串
    const yearStr = currentYear.toString();
    const monthStr = String(currentMonth + 1).padStart(2, '0');
    const dayStr = String(i).padStart(2, '0');
    const dateStr = yearStr + monthStr + dayStr;

    const dayDiv = document.createElement("a");
    dayDiv.href = "entry.php";
    dayDiv.classList.add("day", dateStr);
    dayDiv.textContent = i;

    // 用立即执行函数绑定当前循环的节点和日期
    (function(targetNode, targetDate) {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", `http://localhost/irctclogin/check_date.php?date=${targetDate}`, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && xhr.status === 200) {
          const response = xhr.responseText.trim();
          targetNode.classList.add(response === "exists" ? "booked" : "available");
        }
      };
      xhr.send();
    })(dayDiv, dateStr);

    calendarContainer.appendChild(dayDiv);
  }

  currentMonthYear.textContent = `${months[currentMonth]} ${currentYear}`;
}

// 仅绑定一次按钮点击事件
prevMonthBtn.addEventListener("click", () => {
  currentMonth = currentMonth === 0 ? 11 : currentMonth - 1;
  currentYear = currentMonth === 11 ? currentYear - 1 : currentYear;
  generateCalendar();
});

nextMonthBtn.addEventListener("click", () => {
  currentMonth = currentMonth === 11 ? 0 : currentMonth + 1;
  currentYear = currentMonth === 0 ? currentYear + 1 : currentYear;
  generateCalendar();
});

// 初始化日历
generateCalendar();

内容的提问来源于stack exchange,提问作者Harshvardhan Sinha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 10:01:15