可用原生HTML+JavaScript渲染当月日历:先用getDay()获1号星期几,调整为周一为首;用new Date(year, month+1, 0).getDate()得当月天数;上月补空格数为(firstDayOfWeek + 6) % 7;推荐table而非grid以保障语义、打印及兼容性。
不用框架也能做,核心是算出当月 1 号是周几、当月总天数、再补全前后空白格。关键不是“画表格”,而是正确对齐日期和星期——错一位,整行就偏。
new Date().getDay() 返回的是星期几(0=周日,1=周一),但多数日历以周一为第一天,得手动调整偏移量new Date(year, month + 1, 0).getDate() 拿当月天数(注意:month 是 0 基索引)(firstDayOfWeek + 6) % 7(把周日 0 转成周一 0 的偏移逻辑)innerHTML,优先用 document.createElement 或 DocumentFragment,避免反复重排table 而不是 grid 做月历更稳妥Grid 布局写起来短,但月份跨行/跨周的语义缺失,打印、屏幕阅读器、旧版 Safari(grid 的支持仍有断裂。而 table 天然带行列语义、可访问性好、打印样式稳定。
table 加 role="grid" 和单元格加 role="gridcell",能兼顾 ARIA 标准tr,不能用 div 模拟,否则 NVDA/JAWS 读不出行列关系@supports (display: grid) 里,外面包一层 table 结构兜底click 事件监听不到被动态插入的日期单元格?因为单元格是 JS 动态生成的,直接在 td 上绑 onclick 属性或循环 addEventListener 容易漏绑或重复绑定。委托到父容器最稳。
tbody 或整个 table 上,用 e.target.matches('td[data-date]') 过滤td 必须带唯一标识,推荐用 data-date="2024-04-15",而不是靠 index 或 innerText 解析(中文系统可能格式不一)renderCalendar(),否则点一次刷一遍 DOM,卡顿明显默认 td 高度常设为 40px,但手指触控最小安全尺寸是 44×44px(iOS)或 48×48px(Android)。光改 height 不够,得从盒模型入手。
立即学习“前端免费学习笔记(深入)”;
td 设 min-width: 44px; min-height: 44px;,并用 padding 扩展点击区,别只靠 heighttouch-action: manipulation 减少 iOS 300ms 延迟<meta name="viewport" content="width=device-width, user-scalable=no">,否则用户双击可能意外放大日历new Date() 拿的是本地时间,但后端可能按 UTC 存。显示和提交之间,得明确约定用哪个时区解析 data-date 值。