字幕不显示是因为漏掉结构、属性、VTT格式、服务端响应四个硬性条件之一:track须为video直接子元素且紧贴source后;kind、srclang、label缺一不可;VTT文件需200响应、text/vtt类型、首行WEBVTT、UTF-8无BOM;default不可靠,应监听loadedmetadata后用JS设mode=showing。

写了<track></track>但字幕不显示,不是代码写得不够多,而是四个硬性条件里至少漏了一个——结构、属性、VTT格式、服务端响应,缺一即静默失效,浏览器连错误提示都不给。
track必须紧贴<source></source>之后且是<video></video>的直接子元素
浏览器只在<video></video>开始标签后、所有<source></source>闭合后这个狭窄区间内解析<track></track>。任何偏移都会导致它被完全忽略。
-
<track></track>不能放在<source></source>前面 - 不能写在
<video></video>外部(比如套在<div>里,或紧跟在<code>后面) - 多个
<track></track>之间不要插注释、空行或其它标签(部分浏览器会中断解析) - 正确顺序示例:
<video controls> <source src="movie.mp4" type="video/mp4"> <track kind="subtitles" srclang="zh" label="中文(简体)" src="zh.vtt" default> <track kind="subtitles" srclang="en" label="English" src="en.vtt"> </video>
-
kind必须是"subtitles"(不是"subtitle"、"captions"或留空) -
srclang必须是合法 BCP 47 语言码,如"zh"、"en-US"、"ja";"ch"、"chinese"、"cn"全部无效 -
label是用户在字幕菜单里看到的名字,为空时可能显示"(no label)",无法区分语种 - HTTP 返回状态码必须是
200(本地开发用file://协议必挂,必须起 HTTP 服务) - 服务端必须返回
Content-Type: text/vtt(Nginx/Apache 需显式配置 MIME 类型) - 文件首行必须严格为
WEBVTT(全大写、无空格、无 BOM、无注释) - 编码必须是 UTF-8 无 BOM(VS Code 右下角选 “Save with Encoding” → UTF-8)
- 时间戳格式必须为
00:00:01.234 --> 00:00:04.567(毫秒三位、箭头前后各一个空格) - 更可靠的做法是监听
loadedmetadata事件后手动启用:video.addEventListener('loadedmetadata', () => { const lang = navigator.languages?.[0] || 'zh'; const targetTrack = Array.from(video.textTracks).find(track => track.kind === 'subtitles' && (track.srclang === lang || track.srclang.startsWith(lang.split('-')[0])) ); if (targetTrack) targetTrack.mode = 'showing'; }); - 注意:
textTracks是实时集合,必须等loadedmetadata触发后才能遍历;mode只能设为"disabled"、"hidden"或"showing" - 别在 DOM 就绪前就调
track.mode = 'showing'——此时readyState大概率还是 0,设了也无效
kind、srclang、label三个属性一个都不能少
这不是“建议填写”,而是浏览器识别轨道的硬性字段。缺任意一个,<track></track>就被跳过,video.textTracks里都看不到它。
VTT文件必须同时满足四条格式与服务要求
src指向的.vtt文件,得过四关才算真正可用:
立即学习“前端免费学习笔记(深入)”;
default属性几乎不可靠,真正可控的是 JS 主动设mode
default不是“默认开启”,只是个初始选中信号,实际生效条件极苛刻:首次访问、未操作过字幕开关、仅一个default、srclang匹配系统语言、资源已加载完成……漏一条就白写。
最常被忽略的一点:iOS Safari 会根据srclang和系统语言自动禁用不匹配的轨道,哪怕你写了video::cue样式,它根本没渲染出来,调试时容易误判为 CSS 问题。



















