原生不能当“日历选择器”用,因其仅提供不可定制的弹出式日期选择器,不支持内嵌展示、样式自定义、范围高亮、键盘导航到具体日期格子,且wcag要求的焦点管理、aria标签、键鼠一致操作在其内部为黑盒,无法修改。

为什么原生 <input type="date"> 不能直接当“日历选择器”用
它确实能唤起系统日期面板,但无法自定义样式、不支持范围高亮、不可键盘导航到具体日期格子,更没法在页面中内嵌展示。WCAG 要求的焦点管理、ARIA 标签、键鼠一致操作,在原生控件里是黑盒,你改不了。
所以真要可访问的日历组件,得手写结构 + 严格遵循 WAI-ARIA 日历模式(role="grid" + role="row" + role="gridcell")。
- 必须用
table或语义化div模拟表格布局,但不能真用 HTML<table>(屏幕阅读器会读成“表格”,干扰日历语义) <li>每月标题需带 <code>aria-live="polite",切换月份时自动播报 - 每个日期格子必须有
tabindex="-1",只允许通过方向键进入,禁用鼠标点击直接跳焦(否则会破坏线性导航流) - 当前选中日期要加
aria-selected="true",禁用日期加aria-disabled="true"和tabindex="-1" - 方向键移动时,要跨行/列边界自动折行(比如从第 7 列右箭头 → 第 2 行第 1 列),不是简单加减索引
-
PageUp必须触发完整月份切换逻辑(更新 DOM、重置焦点、触发aria-live播报),不能只改状态 - 所有键盘操作必须同步更新视觉焦点(
el.focus())和 ARIA 状态(aria-selected),二者不同步是常见失败点 - 禁用日期格子收到焦点时,应立即
focus()到下一个可用日期,而不是卡住 - 日期格子用
aria-label="2024年5月12日 星期一"(含完整中文星期,避免只写“Mon”) - 加上
aria-current="date"表示这是当天(非选中),aria-selected="true"表示被选中 - 整个日历容器加
aria-labelledby="calendar-title",而id="calendar-title"的元素内容为“选择预约日期”,形成上下文 - 月份标题(如“2024年5月”)要设
id并被aria-live区域引用,确保切换时播报 - 必须给每个格子加
role="button"+tabindex="0"作为降级方案,并在 JS 中监听click和keydown双事件(iOS Safari 会触发 click 但不触发 keydown) - 对旧版 Edge,用
setTimeout(() => { el.setAttribute('aria-live', 'off'); }, 10)手动刷新 live 区域,绕过缓存 - 所有日期格子必须有明确的
width和height(哪怕只是min-width: 36px),否则 iOS Safari 会把格子压成一条线,焦点框消失
keydown 事件里必须处理哪些方向键和快捷键
仅监听 ArrowUp/ArrowDown/ArrowLeft/ArrowRight 不够——用户按 Home 应该聚焦当月第一天,End 聚焦最后一天,PageUp/PageDown 切换月份,Enter 或 Space 确认选择。
如何让屏幕阅读器正确读出“2024年5月12日,星期一,已选中”
不能只靠 aria-label 拼字符串。得组合使用:
漏掉 aria-current 或混用 aria-label 与 aria-describedby 是导致 NVDA/JAWS 读错的关键原因。
移动端 Safari 和旧版 Edge 的两个隐藏坑
iOS Safari 对 role="gridcell" 的焦点支持极差,经常忽略 tabindex="-1";旧版 Edge(EdgeHTML)不识别 aria-current,且对 aria-live 响应延迟高达 2 秒。
这些不是“兼容性补丁”,而是可访问性的底线——没有焦点框、读不出星期、切月不播报,就等于没做可访问。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











