Firefox中JavaScript视频播放控制条件失效问题求助
问题原因分析
- 高频事件中的异步请求:
timeupdate事件每秒触发多次,每次都调用多个异步fetch请求,Firefox的事件循环处理机制会导致异步结果返回时,视频的currentTime已经超过20秒,错过暂停时机。 - 不必要的async修饰符:
forEach和事件回调中的async会创建未处理的Promise,导致逻辑执行时序混乱,Firefox对这种未处理Promise的处理和Chrome存在差异。 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
相关产品推荐
相关产品推荐

