全屏模态遮罩必须用 position: fixed;真正影响覆盖效果的是 fixed 定位失效或层叠上下文干扰,而非 absolute 本身慢。

遮罩层用 position: fixed 还是 position: absolute?
遮罩是否拖慢覆盖,和定位方式强相关。用 absolute 本身不慢,但若父容器没设 position: relative,遮罩会脱离预期位置,导致你反复调试、加 z-index、补样式——这种“慢”是开发效率慢,不是渲染慢。
真正影响覆盖效果的是:用 fixed 却没配全四边定位;或用了 fixed,但祖先元素有 transform、will-change、filter,触发了新的层叠上下文,让 fixed 失效(它不再相对于视口,而相对于那个新上下文)。
- 全屏模态遮罩,必须用
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; - 避免在
html、body或遮罩任意上级加transform: translateZ(0)这类“隐形合成触发器” - iOS Safari 对
100vh支持不稳定,可改用min-height: 100vh; min-height: -webkit-fill-available;双保险
z-index 设再大也压不住?检查层叠上下文
不是 z-index 不够大,而是它根本没生效。常见于:遮罩父级或同级元素设置了 opacity 、<code>transform、filter、will-change —— 这些都会创建独立层叠上下文,子元素的 z-index 只在该上下文内比较,跨上下文无效。
比如导航栏用了 transform: translateY(0) 做动画优化,结果弹窗一出,就被它盖住——删掉那行 transform,问题立刻消失。
- 用浏览器 DevTools 的“Layers”面板(Chrome)或“3D View”(Firefox)确认是否有意外的层叠上下文
- 遮罩自身不要设
opacity,用background: rgba(0,0,0,0.6)控制透明度 - 慎用
backdrop-filter:Safari 下只要祖先有overflow: hidden或任意transform,它就直接失效
移动端点遮罩关弹窗,为什么点了按钮也关?
这是事件冒泡 + 判断缺失导致的。监听遮罩 click 时,如果只写 overlay.addEventListener('click', close),用户点到弹窗内容区的空白处(比如 padding 区),事件会从弹窗冒泡到遮罩,触发关闭;更糟的是,点弹窗里的按钮,也可能因未阻止冒泡而连带关闭。
关键不是“怎么阻止”,而是“怎么精准识别点击目标”:
- 监听遮罩自身的
click,别绑到document - 判断
e.target === overlay,不能只看e.currentTarget - 弹窗内容区域保持
pointer-events: auto(默认值),不要设none,否则输入框、下拉都失灵 - 遮罩上放关闭按钮?给按钮单独设
pointer-events: auto,遮罩其余部分保持拦截
遮罩一开,页面还能滚?iOS 上尤其明显
单纯给 body 加 overflow: hidden 在 iOS Safari 中经常失效,尤其键盘弹出后页面错位、滚动条卡住。这不是 bug,是 Safari 对 body 滚动控制的限制。
更稳的方案是放弃操作 body,改用视觉锁定:
- 打开遮罩时,记录
document.documentElement.scrollTop,然后给html加position: fixed; top: -${scrollY}px; - 关闭时移除
fixed,并重置scrollTop - 遮罩内部需滚动的内容,单独设
max-height: 80vh; overflow-y: auto; -webkit-overflow-scrolling: touch; - 极端情况可临时加
body { touch-action: none; },但关闭后必须恢复,否则影响所有手势
这些细节里,transform 和层叠上下文最容易被忽略,改完样式发现还是压不住,十有八九是某个看不见的祖先在“偷偷创建上下文”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











