track.mode === 'showing' 且 track.readyState === 2 才表示字幕真正激活并显示;仅 mode 为 showing 不足,因用户未手动启用或资源未加载完成时会自动回退到 disabled。

track.mode 是否等于 'showing' 不代表它真在显示
直接读 track.mode === 'showing' 是最常见误区。浏览器只允许用户手动启用的轨道进入 showing 状态;哪怕你用 JS 设置了 track.mode = 'showing',只要该轨道没被用户在播放器菜单里点过一次,它就会立刻回退到 disabled —— 且不报错、不提示。
真正有效的判断逻辑必须结合两个条件:
-
track.mode === 'showing'(当前模式) -
track.readyState === 2(资源已加载完成,LOADED)
缺一不可。如果 readyState 还是 0(NONE)或 1(LOADING),即使 mode 是 showing,字幕也绝不会出现。
为什么 video.textTracks[n].mode 可能始终是 'disabled'
这不是代码写错了,而是轨道根本没被“授权启用”。浏览器把 track 的激活权交给用户 UI:右键视频 → “字幕”菜单 → 勾选某条轨道,这时它才获得 showing 权限。
立即学习“前端免费学习笔记(深入)”;
常见触发失败场景:
-
<track></track>没写在<video></video>内部,或插在<source></source>中间 →video.textTracks.length为 0 -
srclang值非法(如"chinese"或"zh_CN")→ 轨道被忽略,不出现在菜单中 - VTT 文件 404 或返回
text/plainMIME 类型 →readyState卡在0或3(ERROR) - 本地双击打开 HTML(
file://协议)→ 所有src请求被拦截,readyState永远是0
可靠检测激活状态的最小代码片段
别依赖单次读取,要监听变化 + 等待就绪:
const video = document.getElementById('myVideo');
video.addEventListener('loadedmetadata', () => {
const track = Array.from(video.textTracks).find(t =>
t.kind === 'subtitles' && t.language === 'zh'
);
if (!track) return;
// 等待加载完成再检查
const check = () => {
if (track.readyState === 2 && track.mode === 'showing') {
console.log('轨道已激活并就绪');
return;
}
if (track.readyState < 2) {
setTimeout(check, 100); // 轮询,避免 onload 事件漏掉
}
};
check();
});
注意:textTracks.onchange 事件只在用户手动切换时触发,不能替代 readyState 检查 —— 加载失败时它根本不会触发。
移动端 Safari 的特殊限制
iOS/iPadOS 上,<video></video> 必须带 controls 属性,否则字幕菜单压根不出现,textTracks 列表虽存在但所有 mode 强制为 disabled,且无法通过 JS 绕过。
这意味着:没有 controls,就没有用户启用入口;没有用户启用,mode 就永远不能变成 showing —— 即使你调用 track.mode = 'showing' 成功,下一帧也会被重置。
这个限制极容易被忽略,尤其当你用自定义控件隐藏原生 controls 时,track 就彻底失效了。



















