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

Firefox中JavaScript视频播放控制条件失效问题求助

问题原因分析

  1. 高频事件中的异步请求:timeupdate事件每秒触发多次,每次都调用多个异步fetch请求,Firefox的事件循环处理机制会导致异步结果返回时,视频的currentTime已经超过20秒,错过暂停时机。
  2. 不必要的async修饰符:forEach和事件回调中的async会创建未处理的Promise,导致逻辑执行时序混乱,Firefox对这种未处理Promise的处理和Chrome存在差异。
  3. playing数组维护不严谨:onplay中重复添加视频实例,onpause中没有移除实例,导致数组状态和实际播放状态不一致。

修复方案

1. 预加载所有视频权限数据

在页面加载时一次性获取所有视频的权限信息和会员状态,避免在timeupdate中发起高频请求:

const videos = document.querySelectorAll('.myVideo');
let playing = [];
const videoPermissions = {};
let isMembershipActive = false;

// 预加载视频权限和会员状态
async function preloadData() {
    // 批量获取所有视频权限
    const videoPromises = Array.from(videos).map(video => {
        const videoId = video.getAttribute('data-video-id');
        return Promise.all([
            fetch(`{{ route('video.isFree', ['videoId' => ':videoId']) }}`.replace(':videoId', videoId)).then(res => res.json()),
            fetch(`{{ route('video.isPaid', ['videoId' => ':videoId']) }}`.replace(':videoId', videoId)).then(res => res.json()),
            fetch(`{{ route('video.hasPaid', ['videoId' => ':videoId']) }}`.replace(':videoId', videoId)).then(res => res.json()),
            fetch(`{{ route('video.isMember', ['videoId' => ':videoId']) }}`.replace(':videoId', videoId)).then(res => res.json())
        ]).then(([isFree, isPaid, hasPaid, isMember]) => {
            videoPermissions[videoId] = {
                isFree: isFree.isFree,
                isPaid: isPaid.isPaid,
                hasPaid: hasPaid.hasPaid,
                isMember: isMember.isMember
            };
        });
    });

    // 获取会员状态
    const membershipRes = await fetch(`{{ route('user.isMembershipActive') }}`);
    const membershipData = await membershipRes.json();
    isMembershipActive = membershipData.isMembershipActive;

    // 等待所有视频权限加载完成
    await Promise.all(videoPromises);
}

// 初始化视频控制逻辑
function initVideoControls() {
    videos.forEach((video, index) => {
        const overlay = document.querySelectorAll('.overlay')[index];
        const paymentoverlay = document.querySelectorAll('.paymentoverlay')[index];
        const videoId = video.getAttribute('data-video-id');

        // 直接绑定timeupdate事件,无需等待loadedmetadata
        video.addEventListener('timeupdate', function () {
            if (!isUserLoggedIn) {
                if (video.currentTime >= 20 && !video.paused && playing.includes(video)) {
                    video.pause();
                    video.controls = false;
                    overlay.style.display = 'block';
                }
            } else {
                const permissions = videoPermissions[videoId];
                if (!permissions.isFree) {
                    const canPlay = (permissions.isPaid && permissions.hasPaid) || (permissions.isMember && isMembershipActive);
                    if (!canPlay && !video.paused && video.currentTime >= 20) {
                        video.pause();
                        video.controls = false;
                        paymentoverlay.style.display = 'block';
                    }
                }
            }
        });

        // 准确维护playing数组
        video.onplay = function () {
            if (!playing.includes(video)) {
                playing.push(video);
            }
            video.controls = true;
        };

        video.onpause = function () {
            const idx = playing.indexOf(video);
            if (idx !== -1) {
                playing.splice(idx, 1);
            }
            video.controls = false;
        };
    });

    // 重构点击事件逻辑
    document.addEventListener('click', function (event) {
        if (!event.target.classList.contains('myVideo') && !event.target.classList.contains('play-icon')) {
            videos.forEach((video, index) => {
                const overlay = document.querySelectorAll('.overlay')[index];
                const paymentoverlay = document.querySelectorAll('.paymentoverlay')[index];
                const videoId = video.getAttribute('data-video-id');
                const permissions = videoPermissions[videoId];

                if (!playing.includes(video) && !isUserLoggedIn) {
                    const canPlay = permissions.isFree || permissions.hasPaid || isMembershipActive;
                    if (!canPlay) {
                        video.pause();
                        video.controls = false;
                        overlay.style.display = 'block';
                    } else if (permissions.isPaid && !permissions.hasPaid) {
                        paymentoverlay.style.display = 'block';
                    }
                }
            });
            event.stopPropagation();
        }
    });
}

// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', async () => {
    await preloadData();
    initVideoControls();
});

2. 关键优化点说明

  • 预加载数据:将异步请求移到页面初始化阶段,避免高频事件中的时序问题,同时减少服务器负载。
  • 同步判断逻辑:timeupdate回调改为纯同步操作,确保在事件触发时立即检查视频状态,适配Firefox的事件处理机制。
  • 严谨维护播放状态:在onplay和onpause中准确添加/移除视频实例,保证playing数组状态和实际播放状态一致。
  • 移除不必要的async:避免未处理Promise导致的逻辑混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:18:12