纯JS日历开发问题:双击单元格始终显示同一日期的解决方法
嘿,这问题我之前也踩过坑!本质是闭包作用域的陷阱——你在循环里给单元格绑定双击事件的时候,所有事件回调其实共享了同一个日期变量,等循环结束后这个变量已经变成最后一个日期(29.01.2018)了,所以不管点哪个单元格,都会调用这个最终值。下面给你几种靠谱的修复方案:
解决方案1:用ES6的let声明循环变量(最简单)
如果你的运行环境支持ES6,这是最省心的办法。let会在每次循环迭代时创建一个独立的块级作用域变量,每个回调都会捕获到当前迭代的那个日期值:
// 先定义一个日期格式化辅助函数 function formatDate(date) { const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); return `${day}.${month}.${year}`; } // 修正后的核心逻辑 let currentDate = new Date(2018, 0, 1); // 注意JS月份是0开始,这里是1月1日 const cells = document.querySelectorAll('table td'); for (let i = 0; i < cells.length; i++) { // 关键:复制当前日期对象,避免所有单元格引用同一个Date实例 let cellDate = new Date(currentDate); // 填充单元格日期数字 cells[i].textContent = cellDate.getDate(); // 绑定双击事件,此时每个回调捕获的是当前迭代的cellDate cells[i].addEventListener('dblclick', function() { alert(formatDate(cellDate)); }); // 推进到下一天 currentDate.setDate(currentDate.getDate() + 1); }
解决方案2:用立即执行函数(IIFE)兼容旧环境
如果需要支持不兼容ES6的旧浏览器,可以用IIFE把每次迭代的日期值封装到独立作用域里:
function formatDate(date) { const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); return `${day}.${month}.${year}`; } var currentDate = new Date(2018, 0, 1); var cells = document.querySelectorAll('table td'); for (var i = 0; i < cells.length; i++) { var cellDate = new Date(currentDate); cells[i].textContent = cellDate.getDate(); // 用IIFE把当前cellDate传入,创建独立作用域 (function(selectedDate) { cells[i].addEventListener('dblclick', function() { alert(formatDate(selectedDate)); }); })(cellDate); currentDate.setDate(currentDate.getDate() + 1); }
解决方案3:把日期存到单元格自定义属性里
另一种不依赖闭包的思路:把格式化后的日期字符串存在单元格的data-date属性中,点击时直接读取这个属性:
function formatDate(date) { const day = date.getDate().toString().padStart(2, '0'); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const year = date.getFullYear(); return `${day}.${month}.${year}`; } var currentDate = new Date(2018, 0, 1); var cells = document.querySelectorAll('table td'); for (var i = 0; i < cells.length; i++) { const dateStr = formatDate(currentDate); cells[i].textContent = currentDate.getDate(); // 把日期存到自定义属性里 cells[i].dataset.date = dateStr; // 点击时读取当前元素的data-date属性 cells[i].addEventListener('dblclick', function() { alert(this.dataset.date); }); currentDate.setDate(currentDate.getDate() + 1); }
关键提醒
不管用哪种方案,都要创建Date对象的副本(比如new Date(currentDate)),而不是直接引用currentDate——因为Date是引用类型,如果不复制,所有单元格的日期都会指向同一个对象,最后都会变成循环结束时的最后一个日期。
内容的提问来源于stack exchange,提问作者user9317875
相关产品推荐
相关产品推荐

