jQuery日历显示异常及日期状态类错误赋值问题求助
嗨,看你卡这个问题三周了,太闹心了!我帮你梳理下代码里的几个核心问题,这些应该就是导致各种诡异BUG的根源:
1. 最致命的异步执行顺序错误
你现在的代码里,生成calendarAvailabilities的for循环和getOpenDays(cal)调用是在AJAX请求的回调函数外面!AJAX是异步操作——浏览器发起请求后不会等它返回,直接就去执行后面的代码了。这时候回调里的DOM修改(加/减fc-closedDay类)根本还没跑,你用的还是之前的旧DOM状态,这就导致日历数据总是错位,切换月份时混合新旧数据。
修复方法:把for循环和getOpenDays(cal)完全移到AJAX的回调函数内部,确保只有当数据返回、DOM状态更新完成后,才去生成日历数据。
2. DOM选择器的模糊匹配问题
你用contains('" + d + "')匹配日期时,比如d=5,页面上所有包含"5"的日期(15、25、5号)都会被命中,直接导致错误的日期被加上/移除fc-closedDay类——这就是为什么有时候所有div都被标成闭店的原因。另外,法语(Lun/Mar)和英语(Mon/Tue)的星期标签混用,也可能导致选择器匹配异常。
修复方法:
- 优先给每个日期div加精确的
data-date属性(比如data-date="2024-05-05"),用属性选择器精确匹配; - 如果没法改DOM结构,就用
filter做精确文本匹配:// 精确匹配日期数字,而非包含 jQuery("span.fc-date").filter(function() { return $(this).text().trim() === d.toString(); })
3. 月份索引的日期处理错误
JavaScript的Date对象月份是从0开始的(0=1月,11=12月),你直接用cal.getMonth()转成两位数字生成日期字符串,会导致月份整体偏移一个月(比如cal.getMonth()返回0,转成"01"实际对应二月),这会让calendarAvailabilities的键完全错误,日历自然显示不对。
修复方法:生成日期时给月份加1,修正索引偏移:
const actualMonth = cal.getMonth() + 1; // 转成1-12的实际月份 const formattedMonth = number.format(actualMonth);
4. 未清除旧的状态类
切换月份时,上一个月份的fc-closedDay类没有被清除,新的类直接叠加,导致DOM状态混乱,混合了多个月份的闭店数据。
修复方法:在AJAX回调开始时,先清除所有日期div的旧状态:
function(data) { // 先清空旧的闭店标记 jQuery("div.fc-closedDay").removeClass('fc-closedDay'); // 再处理新数据... }
修正后的核心代码示例
function getClosingDays(cal) { // 获取闭店日期 jQuery.get( "index.php?option=com_skydivespa&task=reservation.getClosingDays", {"month": cal.getMonth(), "year": cal.getYear()}, function(data) { console.log(data); const periods = JSON.parse(data); // 第一步:清除所有旧的闭店状态 jQuery("div.fc-closedDay").removeClass('fc-closedDay'); if (periods['data'].length === 1) { // 处理单周期情况,用映射优化代码 const weekdayMap = { mon: "Lun", tue: "Mar", wed: "Mer", thu: "Jeu", fri: "Ven", sat: "Sam", sun: "Dim" }; Object.entries(weekdayMap).forEach(([key, label]) => { if (periods['data'][0][key] === 0) { jQuery("span.fc-weekday").filter(function() { return $(this).text().trim() === label; }).parent("div").addClass('fc-closedDay'); } }); } else { // 处理多周期情况 for (let i = 0; i < periods.data.length; i++) { const dateStartPeriod = new Date(periods['data'][i].date_start); const dateEndPeriod = new Date(periods['data'][i].date_end); const weekdayMap = { mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", fri: "Fri", sat: "Sat", sun: "Sun" }; Object.entries(weekdayMap).forEach(([key, label]) => { const isClosed = periods['data'][i][key] === 0; // 遍历周期内的每一天 for (let d = dateStartPeriod.getDate(); d <= dateEndPeriod.getDate(); d++) { // 精确匹配日期+星期的组合 const dateEl = jQuery("span.fc-date").filter(function() { return $(this).text().trim() === d.toString(); }).next("span.fc-weekday").filter(function() { return $(this).text().trim() === label; }).parent("div"); isClosed ? dateEl.addClass('fc-closedDay') : dateEl.removeClass('fc-closedDay'); } }); } } // 生成日历可用数据(确保DOM已更新) const calendarAvailabilities = {}; const numberFormatter = Intl.NumberFormat('en-US', {minimumIntegerDigits: 2}); const year = cal.getYear(); const formattedMonth = numberFormatter.format(cal.getMonth() + 1); // 修正月份索引 for (let d = 1; d <= 31; d++) { const formattedDay = numberFormatter.format(d); const dateKey = `${formattedMonth}-${formattedDay}-${year}`; const dateEl = jQuery("span.fc-date").filter(function() { return $(this).text().trim() === formattedDay; }).parent("div"); calendarAvailabilities[dateKey] = dateEl.hasClass('fc-closedDay') ? ['<span class="close uk-button uk-button-small uk-button-primary uk-margin-small">Close</span>'] : ['<span class="free uk-button uk-button-small uk-button-primary uk-margin-small" onclick="validateResaStep1(\'9:00\')" slot="9:00:00">9:00-10:00</span>']; } // 更新日历数据 cal.setData(calendarAvailabilities); // 在DOM更新后调用getOpenDays getOpenDays(cal); } ); }
额外优化:用Async/Await简化异步逻辑
如果想避免回调嵌套,还可以把jQuery的AJAX转成Promise用async/await:
async function getClosingDays(cal) { try { // 转成Promise,用await等待请求返回 const data = await jQuery.get( "index.php?option=com_skydivespa&task=reservation.getClosingDays", {"month": cal.getMonth(), "year": cal.getYear()} ).promise(); // 后面的处理逻辑和上面一致 } catch (err) { console.error("获取闭店日期失败:", err); } }
备注:内容来源于stack exchange,提问作者Sephirah

