You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复网页视频字幕切换时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>

主要修改点

  1. 新增subtitleEnabled变量控制字幕开关状态,逻辑清晰不会出现遍历中修改状态的问题。
  2. 页面加载完成后一次性解析VTT并添加所有cue到轨道中,不需要每次切换都重复解析。
  3. 直接修改TextTrack的mode属性控制显隐,完全避免了增删cue可能出现的对象匹配异常,性能也更优。

内容的提问来源于stack exchange,提问作者Gilby1385

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 02:57:05