是独立堆叠上下文则父级z-index失效;检查iframe及其父节点的stacking context、transform(含translateZ(0))、opacity等触发属性。

用 DevTools 直接定位遮挡源
别猜,直接看。打开浏览器开发者工具,选中 iframe 元素,切到「Computed」面板,搜 stacking context —— 如果显示 Yes,说明它自己或某个祖先已创建独立堆叠上下文,父页面的 z-index 就失效了。
再往上逐层点击它的父节点(尤其是最近的非 body 父容器),同样查 stacking context 和以下属性:
-
transform(哪怕只是translateZ(0)) -
opacity(只要 0.99) -
filter(哪怕blur(0)) -
will-change(设了任意值都触发) -
position: relative+z-index: 0(注意:不是auto,是显式写 0)
临时禁用这些样式,iframe 内的下拉框、日期选择器立刻能弹出来,就锁定问题点了。
为什么 iframe 内元素总被裁切,而不是“盖不住”
这不是 z-index 数值不够大,而是 iframe 渲染在完全隔离的堆叠上下文中 —— 它的 <select>、el-date-picker 面板、子页面自己的 dialog,全被锁死在 iframe 的视口边界里。滚动后遮挡更明显,是因为父容器没参与层级竞争,导致 iframe 内容和外部蒙层根本不在一个“平面”上。
立即学习“前端免费学习笔记(深入)”;
关键检查点:
- iframe 标签本身不能设
z-index(它不是定位元素) - 必须给它的**直接父容器**加
position: relative或absolute,并配z-index - 该父容器不能带任何触发 stacking context 的属性(见上一条列表)
- 如果父 modal 是
position: fixed,iframe 父容器也得是position: relative或fixed,否则层级链断裂
iframe 里的遮罩层为啥盖不住父页面按钮
因为子页面的遮罩永远困在 iframe 的 DOM 范围内,它再高也出不去。你看到“按钮还能点”,不是层级没压住,是遮罩根本没覆盖到父页面区域。
正确做法是:子页面通过 window.parent.postMessage({ type: 'MODAL_OPEN' }, 'https://trusted-origin.com') 通知父页,由父页在 document.body 最外层插入真实遮罩层(position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;),并监听 scroll 和 resize 动态更新高度。
常见错误:
- 用
v-if或useState控制遮罩显隐,却把遮罩写在组件内部 —— Vue/React 的 portal 机制可能把它挂错位置 - 生产环境用
"*"做postMessagetargetOrigin,有 XSS 风险 - 遮罩层没设
pointer-events: none,反而拦截了 iframe 内部操作
动态设置 iframe.src 时为什么遮挡会突然加重
不是样式变了,是 DOM 重排触发 iframe 重载 —— 典型写法:modal.style.display = 'none' → 改 src → modal.style.display = 'block'。浏览器在 display 切换瞬间强制 reload iframe,导致渲染树重建、堆叠上下文重置,原本勉强可用的层级关系彻底乱掉。
修复方式只有两个靠谱的:
- 先让容器可见:
modal.style.display = 'block',再赋值:iframe.src = url - 用
iframe.hidden = true替代display: none,隐藏时不触发重排
Vue/React 中尤其危险:useEffect 依赖项顺序错乱、或 v-if 切换时机不对,都会复现这个现象。真机测试时 iOS Safari 表现最差,模拟器常看不出问题。
stacking context 是不是又冒出来了。



















