如何修复网页视频字幕切换时removeCue未找到的DOMException错误
错误根因
- 每次切换字幕时都重新解析VTT内容生成新的
VTTCue实例,新实例和已添加到字幕轨道中的实例不是同一个对象引用,调用removeCue时无法匹配到对应条目,直接抛出DOM异常。 - 状态控制逻辑存在漏洞:
subs变量的重置操作放在了遍历cue的回调函数中,第一个cue处理完就将subs置为0,后续所有cue的处理逻辑都会完全错乱。 - 重复解析VTT内容属于不必要的性能损耗,解析一次缓存结果即可。
修复方案
我们可以利用TextTrack原生的mode属性控制字幕显隐,不需要反复增删cue实例,逻辑更简单也不会出现对象匹配异常的问题,修改后完整代码如下:
<html> <head> <title>Subtitle Toggle</title> </head> <center> <video controls id="movie"> <source src="https://www.w3schools.com/html/movie.mp4" type="video/mp4"> </video><br/><br/> <button onclick="subtitles()">Toggle Subtitles</button> </center> <!-- Subtitles --> <script type="text/vtt" id="sub"> WEBVTT 1 00:00:01.000 --> 00:00:03.000 Subtitle line 1 2 00:00:04.000 --> 00:00:05.000 Subtitle line 2 3 00:00:06.000 --> 00:00:10.000 Subtitle lines 3 and 4 </script> <script> // 初始化字幕轨道,默认隐藏 var trk = movie.addTextTrack('subtitles'); trk.mode = "hidden"; var subtitleEnabled = false; // Get around "VTT not displaying" bug function parse_timestamp(s) { // Relaxing the timestamp format: var match = s.match(/^(?:([0-9]+):)?([0-5][0-9]):([0-5][0-9](?:[.,][0-9]{0,3})?)/); if (match == null) { throw 'Invalid timestamp format: ' + s; } var hours = parseInt(match[1] || "0", 10); var minutes = parseInt(match[2], 10); var seconds = parseFloat(match[3].replace(',', '.')); return seconds + 60 * minutes + 60 * 60 * hours; } function quick_and_dirty_vtt_or_srt_parser(vtt) { var lines = vtt.trim().replace('\r\n', '\n').split(/[\r\n]/).map(function(line) { return line.trim(); }); var cues = []; var start = null; var end = null; var payload = null; for (var i = 0; i < lines.length; i++) { if (lines[i].indexOf('-->') >= 0) { var splitted = lines[i].split(/[ \t]+-->[ \t]+/); if (splitted.length != 2) { throw 'Error when splitting "-->": ' + lines[i]; } // Already ignoring anything past the "end" timestamp (i.e. cue settings). start = parse_timestamp(splitted[0]); end = parse_timestamp(splitted[1]); } else if (lines[i] == '') { if (start && end) { var cue = new VTTCue(start, end, payload); cues.push(cue); start = null; end = null; payload = null; } } else if (start && end) { if (payload == null) { payload = lines[i]; } else { payload += '\n' + lines[i]; } } } if (start && end) { var cue = new VTTCue(start, end, payload); cues.push(cue); } return cues; } // 页面加载完成后一次性添加所有字幕cue window.onload = function() { var cues = quick_and_dirty_vtt_or_srt_parser(sub.innerHTML); cues.forEach(function(cue) { trk.addCue(cue); }); } function subtitles() { subtitleEnabled = !subtitleEnabled; trk.mode = subtitleEnabled ? "showing" : "hidden"; } </script> </html>
主要修改点
- 新增
subtitleEnabled变量控制字幕开关状态,逻辑清晰不会出现遍历中修改状态的问题。 - 页面加载完成后一次性解析VTT并添加所有cue到轨道中,不需要每次切换都重复解析。
- 直接修改
TextTrack的mode属性控制显隐,完全避免了增删cue可能出现的对象匹配异常,性能也更优。
内容的提问来源于stack exchange,提问作者Gilby1385
相关产品推荐
相关产品推荐

