z-index 失效主因是层叠上下文分裂,而非数值设置错误;全屏播放器创建新层叠上下文后,其子元素层级只在内部比较,需将导航栏与播放器容器置于同一 position: fixed 父容器中统一管理 z-index。

z-index 失效是因为创建了多个层叠上下文
固定导航栏被全屏视频播放器盖住,不是 z-index 没写对,而是播放器(比如 xgplayer)在进入全屏时会动态创建一个新容器(通常是 div.xgplayer-fullscreen),这个容器自带 position: fixed 和独立的 z-index。一旦它创建了新的层叠上下文(例如加了 transform、opacity 或显式 z-index),它的子元素就只和它内部比层级,不再和你页面顶层的 .navbar 比。
常见错误是给导航栏设 z-index: 9999,却没意识到播放器全屏层可能在另一个层叠上下文中以 z-index: 1 就压过了你——因为它的父上下文本身层级更高。
- 检查播放器全屏容器是否带
transform: translateZ(0)或will-change: transform,这些都会隐式创建层叠上下文 - 用浏览器开发者工具的「Layers」面板(Chrome DevTools → Rendering → Layers)确认两个元素是否处于同一层叠上下文
- 不要只看单个
z-index值,要顺着 DOM 树往上查,直到找到最近的共同层叠上下文根节点
必须让导航栏和播放器全屏层同属一个层叠上下文
最稳的做法,是把导航栏和播放器都放进同一个 position: fixed 容器里,由这个父容器统一管理堆叠顺序。这样它们就在同一层叠上下文中,z-index 才真正可比。
示例结构:
立即学习“前端免费学习笔记(深入)”;
<div class="viewport-overlay"> <nav class="navbar"></nav> <div id="player-container"></div> </div>
对应 CSS:
.viewport-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
z-index: 1000; /* 统一层级锚点 */
}
.navbar {
z-index: 2; /* 在 overlay 内部排第二 */
}
#player-container {
z-index: 1; /* 在 overlay 内部排第一,但内容仍可被 navbar 盖住 */
}- 避免在
.viewport-overlay或其子元素上使用transform、filter、opacity < 1,否则又会分裂层叠上下文 - 播放器初始化时,用 JS 把它的根容器移入
.viewport-overlay,而不是留在body底下 - 如果播放器强制挂载到
body(如某些版本xgplayer的fullscreenTarget默认行为),需手动重绑定:调用player.destroy()后重新new Player({ container: document.querySelector('#player-container') })
移动端横屏全屏时右侧控制栏被虚拟导航键遮挡
这不是 CSS 层级问题,是坐标系统错位。横屏下 Android 的三键导航栏会旋转到屏幕右侧,而播放器默认把控制栏右对齐到「物理屏幕右边缘」,结果整个控制区直接落在导航键区域里。
修复关键不是调 z-index,而是让控制栏感知安全区域:
- 必须在
<meta>中启用:<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> - 控制栏容器加:
padding-right: env(safe-area-inset-right, 0px);,并用@supports (padding-right: env(safe-area-inset-right))包裹 - 禁用「仅隐藏状态栏」的方案——横屏遮挡来自导航栏,不是状态栏;需调用
WindowInsetsController(Android)或UIStatusBarManager(iOS)隐藏整套系统栏 - JS 层监听
visualViewport变化,在横屏且env(safe-area-inset-right) > 0时,动态将控制栏左移getComputedStyle读出的实际 inset 值
全屏退出后导航栏位置错乱或消失
这是播放器退出全屏时未还原 DOM 状态导致的。典型表现:退出后导航栏还在,但 top: 0 失效、z-index 被清空、甚至整个 <nav> 被移出文档流。
根本原因是播放器全屏逻辑常会临时修改 body 的 overflow、height,或把导航栏 display: none,但没在 exitFullscreen 回调中恢复。
- 不要依赖播放器自带的
exitFullscreen事件做 UI 还原——有些版本该事件触发时机早于 DOM 回滚完成 - 改用
document.addEventListener('fullscreenchange', () => { ... }),并在回调中检查document.fullscreenElement === null再执行还原 - 还原动作必须包含:
body.style.overflow = ''、nav.style.display = 'block'、nav.style.zIndex = '1000'(不要用removeAttribute,IE 不兼容) - 若导航栏使用了
transform动画,退出全屏后需强制重排:在还原后加一句nav.offsetHeight触发回流
复杂点在于,不同播放器(xgplayer、video.js、hls.js 自研封装)对全屏状态的接管方式完全不同,有的劫持 requestFullscreen,有的自己模拟全屏样式。没有银弹方案,必须按实际使用的播放器源码逻辑去 patch 它的全屏生命周期钩子。



















