JavaScript视频观看时长统计:浏览器关闭场景下的事件处理问题
解决视频观看时长统计的场景问题
嘿,这个问题我做视频平台统计的时候也踩过坑!确实,ended事件只在视频正常播放完毕时触发,用户中途关浏览器、切标签页都不会触发,而每秒上报又太耗资源。给你分享几个实用的方案,结合事件和合理的上报策略就能搞定:
核心适配事件推荐
1. beforeunload / unload 事件
这两个事件是专门处理页面卸载场景的:
beforeunload在页面即将卸载(比如关闭浏览器、刷新、跳转)时触发,兼容性更好,也更可靠;unload则是在页面已经开始卸载时触发,但部分浏览器会限制其中的异步操作。
重点:用navigator.sendBeacon上报
普通的异步请求(比如fetch、XHR)在页面卸载时很可能被浏览器中断,而navigator.sendBeacon是浏览器专门为这类“离开时上报”场景设计的API——它会异步发送请求,但不会阻塞页面卸载,能最大程度保证请求送达服务器。
2. visibilitychange 事件
用户切换标签页、最小化窗口时,视频会自动暂停,但这段观看时长也需要统计。监听visibilitychange事件,当页面从可见变为隐藏时,立即上报当前的观看进度,这样可以避免用户切走后很久才关闭浏览器,导致中间的时长丢失。
结合事件的最优上报策略
把这些事件和兜底的定时上报结合,既能保证数据准确,又能控制上报频率:
- 保留
ended事件,处理视频正常看完的场景; - 用
visibilitychange处理标签切换/窗口最小化的场景; - 用
beforeunload处理关闭浏览器/刷新的场景; - 可选:每30-60秒做一次定时上报作为兜底,防止极端情况下事件未触发(比如浏览器崩溃)。
简单实现示例
const video = document.getElementById('target-video'); let lastReportedTime = 0; // 记录上次上报的时长,避免重复上报 // 封装上报逻辑 function submitWatchTime() { const currentPlayTime = Math.floor(video.currentTime); // 只上报新增的观看时长 if (currentPlayTime > lastReportedTime) { const payload = JSON.stringify({ videoId: 'your-video-id', addedWatchTime: currentPlayTime - lastReportedTime }); // 用sendBeacon发送,确保请求能被处理 navigator.sendBeacon('/api/watch-time/report', payload); lastReportedTime = currentPlayTime; } } // 视频正常播放结束 video.addEventListener('ended', submitWatchTime); // 页面切换到后台时上报 document.addEventListener('visibilitychange', () => { if (document.hidden) { submitWatchTime(); } }); // 页面卸载前上报 window.addEventListener('beforeunload', submitWatchTime); // 可选:兜底定时上报,每30秒一次 setInterval(submitWatchTime, 30000);
注意事项
navigator.sendBeacon默认是POST请求,数据会以application/x-www-form-urlencoded格式发送,如果需要JSON格式,可能需要调整后端接收逻辑,或者用Blob包装数据;beforeunload事件里不要执行太复杂的逻辑,浏览器给的执行时间有限,尽量保持上报逻辑简洁;- 统计时长时建议取整(比如
Math.floor),避免小数点后的精度问题。
内容的提问来源于stack exchange,提问作者Adib
相关产品推荐
相关产品推荐

