safari 的 z-index 对 video 无效是因 ios/ipados 将其强制渲染在原生控件层,不参与层叠上下文;需同时设 webkit-playsinline/ playsinline、transform: translatez(0) 和 will-change: transform 触发合成层,并作用于 video 自身且配 position: relative 与 z-index。

为什么 Safari 的 z-index 对 <video></video> 完全无效
这不是 CSS 写错了,而是 Safari(尤其是 iOS/iPadOS)把 <video></video> 当作“原生控件层”强制渲染在 WebView 内容层之上。它压根不参与标准的层叠上下文计算——哪怕父容器设了 position: relative 和 z-index: 9999,视频仍会穿透所有 fixed 或 absolute 元素。
webkit-playsinline 是前提,不是解药
这个属性只控制播放行为:让视频留在页面内播放,而非自动全屏。但它本身不改变层级。
- 必须同时写
webkit-playsinline和playsinline(双写更稳妥) - 仅 iOS 10+ 和部分 macOS Safari 有效;不加则点击即全屏,彻底脱离 DOM 层级
- 加了之后,视频仍在原生层,但至少保留在视口内,为后续合成层 hack 提供操作空间
真正起效的是 transform: translateZ(0) + will-change: transform
这两个属性组合能触发 Safari 的独立 GPU 合成层(compositing layer),让 <video></video> 脱离默认的“最高原生层”,进入可被 z-index 管理的渲染通道。
- 必须作用在
<video></video>自身,不是父容器 - 必须搭配
position: relative和明确的z-index(例如z-index: 1)才生效 - 若多个视频重叠,它们的
z-index值需有差异,且父容器不能有overflow: hidden截断合成层 - 旧版 Safari(iOS 12 之前)可能还需加
-webkit-backface-visibility: hidden
微信浏览器和 uni-app 的 video 不是同一个问题
微信浏览器里 <video></video> 遮挡元素,本质是 WebView 渲染策略不同,position: fixed + 更高 z-index 往往就能解决;而 uni-app 在 App 端或小程序里,<video></video> 是原生组件,直接位于 WebView 之上,CSS 层级完全失效——此时只能用 cover-view(小程序)或 subNVue(App)这类平台专属覆盖方案。
最易被忽略的一点:所有这些方案都依赖“视频未全屏”的前提。一旦用户点击播放、系统接管全屏逻辑,所有 CSS 和 DOM 层级控制都会瞬间失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











