video 和 audio 元素必须显式启用 controls 属性,否则屏幕阅读器无法识别播放控件;track 元素需同时设置 srclang 和 label,且 kind 语义不可混用;所有自定义控件须支持键盘操作、动态 aria 属性及精准 aria-live 报播。

video 和 audio 元素必须显式启用 controls 属性
不加 controls,浏览器不会渲染任何播放控件,屏幕阅读器也无从识别播放/暂停、音量等状态。即使你后续用 JavaScript 自定义 UI,也要先保留原生控件作为可访问性锚点——否则 aria-pressed、aria-label 等属性将失去上下文依据。
常见错误:开发者为“统一 UI 风格”直接移除 controls,再用纯 <button></button> 模拟全部功能,却忘了给每个按钮补全 role="button"、aria-label 和键盘焦点逻辑。
- 若必须隐藏原生控件,请用
display: none或visibility: hidden,不要用removeAttribute('controls') - 始终确保播放/暂停按钮有动态
aria-pressed值(true或false) - 进度条需同时支持鼠标拖拽和键盘方向键(
←/→调整 1 秒,↑/↓调整 5 秒)
track 元素的 srclang 和 label 缺一不可
<track> 不是“加了就行”,缺少 srclang 会导致屏幕阅读器无法判断语言,缺失 label 则播放器下拉菜单里显示为空白项或 “undefined”。用户根本不知道哪条是中文、哪条是英文字幕。
更关键的是:kind="subtitles" 和 kind="captions" 语义不同——前者面向听障者(含环境音、说话人标识),后者面向外语学习者(仅翻译对白)。混用会误导辅助技术行为。
立即学习“前端免费学习笔记(深入)”;
- 每条字幕轨必须带
srclang(如zh、en)和label(如中文(简体)) - 默认启用一条时,只给其中一条加
default,多条都加会触发未定义行为 -
kind="descriptions"轨道目前仅被部分屏幕阅读器识别,建议同步提供带内嵌描述的备用视频链接
键盘操作必须覆盖全部控件链
Tab 键顺序不能跳过音量滑块、静音按钮、字幕开关这些“次要”控件。很多页面只让播放/暂停按钮可聚焦,其余靠鼠标悬停展开,这对键盘或屏幕阅读器用户等于功能阉割。
尤其注意:自定义进度条若用 <div class="progress"> 实现,必须手动添加 tabindex="0",并监听 keydown 事件处理方向键和 Enter 确认,否则它在 Tab 流中完全不可达。
- 所有控件需响应
Space和Enter(二者语义等价,不可只支持其一) - 静音按钮切换时,应同步更新
aria-label(如从“取消静音”变为“已静音”) - 字幕开关按钮需用
aria-haspopup="listbox"+aria-expanded表达下拉状态
aria-live 区域要精确定义播报粒度
用 aria-live="polite" 吐出“当前时间 23 秒”听起来很贴心,但如果每秒都播报,会严重干扰屏幕阅读器用户听正文。真正该播报的是**状态突变**,不是轮询。
比如:用户拖动进度条后松手,才播报“已跳转至 1 分 42 秒”;点击静音按钮后,才播报“已静音”;字幕轨道切换后,才播报“已启用英文字幕”。连续播报 = 噪声。
- 避免在
timeupdate事件里直接写liveRegion.textContent = ... - 使用
aria-atomic="true"确保整句播报不被截断 - 不要把播放进度、音量、字幕状态塞进同一个
aria-live区域——它们变更频率和语义完全不同
最常被忽略的一点:所有自定义控件的 aria-label 必须包含当前上下文,例如“播放:当前进度 45 秒”,而不是静态的“播放按钮”。用户靠语音反馈定位操作结果,静态文案等于没说。



















