default属性仅表示字幕轨道可被默认启用,而非自动显示;浏览器仍需用户手动点击CC按钮,且Safari完全忽略default,必须用JS在loadedmetadata事件中设track.mode="showing"。

default属性根本不会让字幕“自动显示”
加了 default,字幕还是不出现——这不是配置错了,是理解错了它的作用。default 只表示“这个轨道可被默认启用”,不是“一加载就渲染”。浏览器仍要求用户手动点播放器上的 CC 按钮,否则 track.mode 始终是 "disabled",哪怕 DOM 里写了 default。Chrome、Firefox、Safari 全部遵守这一行为,没有例外。
为什么写了default但textTracks[i].mode还是disabled
常见原因不是代码漏写,而是底层条件没满足:
-
kind必须严格等于"subtitles"(不能是"subtitle"、"captions"或带空格) -
srclang必须是合法 BCP 47 码(如"zh"、"zh-Hans";"chinese"、"zh_CN"会静默失败) -
<track>必须作为<video>的**直接子元素**,且在所有<source>之后、</video>之前 - VTT 文件返回 HTTP 200 且响应头含
Content-Type: text/vtt(Nginx/Apache 需显式配置 MIME 类型) - 本地开发时用
file://协议打开 HTML → 所有.vtt加载静默失败,必须起本地服务(如python3 -m http.server)
真正能让字幕一加载就显示的 JS 写法
靠 default 不可靠,必须用 JS 主动设 track.mode = "showing",但时机和条件很关键:
- 监听
loadedmetadata事件(不是DOMContentLoaded),此时video.textTracks才可用 - 检查
track.readyState === 2(即TextTrack.LOADED),避免设在加载中途 - 优先匹配
navigator.languages,再 fallback 到document.documentElement.lang - 设完目标轨道后,把其他同
kind="subtitles"的轨道设为"disabled",防止多轨叠加
示例:
立即学习“前端免费学习笔记(深入)”;
video.addEventListener('loadedmetadata', () => {
const lang = navigator.languages?.[0] || document.documentElement.lang || 'zh';
const target = Array.from(video.textTracks).find(t =>
t.kind === 'subtitles' &&
(t.srclang === lang || t.srclang.startsWith(lang.split('-')[0]))
);
if (target && target.readyState === 2) {
target.mode = 'showing';
Array.from(video.textTracks)
.filter(t => t !== target && t.kind === 'subtitles')
.forEach(t => t.mode = 'disabled');
}
});
移动端 Safari 的 default 完全无效
这是最常被忽略的硬限制:Safari(包括 iOS 和 macOS)在任何场景下都忽略 default 属性,track.mode 初始值恒为 "disabled"。即使你写了 <track kind="subtitles" srclang="zh" default>,它也不会出现在右键菜单或控件栏里——除非你用 JS 显式设 mode = "showing",且必须在 loadedmetadata 后执行。另外,Safari 私密模式下甚至可能跳过整个 <track> 解析,连节点都不创建。



















