video退出全屏后位置丢失是浏览器标准行为,因全屏时元素脱离文档流且滚动状态未同步;需监听fullscreenchange事件,用getBoundingClientRect获取坐标并scrollBy补偿偏移,同时注意框架ref失效与CSS transform重置问题。

HTML5 全屏退出后位置丢失,本质是浏览器在全屏切换时重排了元素布局,而视频容器本身未做定位保护或状态同步 —— 这不是 bug,是标准行为,但可干预。
为什么 video 退出全屏后会“跳位”或“回到顶部”
主流浏览器(Chrome/Firefox/Safari)在调用 requestFullscreen() 时,会将 提升为顶层浮动元素,并脱离文档流;退出时虽恢复 DOM 位置,但若其父容器有 position: fixed、transform、overflow: hidden 或 CSS 动画正在运行,就可能触发重绘错位。更常见的是:滚动位置未保存,页面被强制回滚到 初始位置(尤其当它位于长页面中下部时)。
-
document.fullscreenElement变更后,未监听fullscreenchange事件做补偿 - 使用了
video.parentNode.scrollTo()或window.scrollTo()但坐标计算错误 - Vue/React 等框架中,
video被包裹在条件渲染区块(如v-if/useState)里,全屏退出触发了组件重挂载
fullscreenchange 事件里必须做的三件事
这是修复位置丢失最直接的入口。不要只监听,要主动同步状态:
- 退出全屏时(
document.fullscreenElement === null),立即用getBoundingClientRect()记录原video元素视口坐标,再用window.scrollBy()补偿偏移(避免用scrollTo()硬设,易冲突) - 若页面启用了平滑滚动(
CSS scroll-behavior: smooth),需临时关闭再恢复,否则scrollBy会延迟生效 - 检查
video是否被transform或scale缩放过 —— 全屏时这些样式常被重置,退出后需手动还原
示例关键逻辑:
立即学习“前端免费学习笔记(深入)”;
document.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
const rect = video.getBoundingClientRect();
window.scrollBy(0, rect.top - window.innerHeight / 2 + rect.height / 2);
}
});React/Vue 中的特殊陷阱:ref 失效与 key 冲突
框架对 DOM 生命周期的接管会让全屏行为更难预测:
- 不要把
video放在key动态变化的内 —— 全屏退出可能触发重新渲染,导致 ref 指向新节点,旧状态丢失- Vue 用户慎用
v-show控制视频显隐:它仅切display,但全屏 API 要求元素必须在文档流中可见且非visibility: hidden- React 中若用
useEffect绑定fullscreenchange,务必清理事件监听器,否则多次挂载会叠加监听,造成重复滚动真正稳定的替代方案:不依赖原生全屏,用 CSS + JS 模拟
当兼容性或控制粒度要求高时,放弃
requestFullscreen()是更稳妥的选择:- 给
video容器加position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 9999;,并用 JS 切换 class 控制显示/隐藏 - 模拟全屏时,用
video.play()+video.muted = false(注意 iOS 需用户手势触发) - 退出时,先恢复容器
position: relative,再用scrollIntoView({ behavior: 'smooth', block: 'center' })定位回原位置
这个方案绕开了浏览器全屏 API 的布局劫持,所有状态完全可控,尤其适合嵌入式播放器或弹窗类场景。
真正麻烦的从来不是“怎么进全屏”,而是“怎么退得干净”。位置丢失背后,往往是滚动状态、CSS 层叠上下文、框架生命周期三者没对齐 —— 抓住
fullscreenchange这个钩子,再盯紧 scroll 和 transform 的联动,基本就能稳住。 - Vue 用户慎用











