z-index 调到最大也不管用?你撞上「层叠上下文」了
有天下午,我写一个下拉菜单,死活浮不起来。明明 z-index: 9999 都加上了,它还是乖乖躺在页面底下,像个社恐。
后来才知道,问题不在 z-index 的大小,而在「层叠上下文」。
层叠上下文是什么
想象一群人叠罗汉。你在一个罗汉堆里拼命把手举高(调 z-index),但另一个罗汉堆整体比你高,那你再怎么举也没用——你被你所在的那个堆「封顶」了。
在 CSS 里,每个元素最终是按「它属于哪个层叠上下文」来比高低的,不是全局比。同一个上下文内部比 z-index;不同上下文之间,比的是它们父级谁高。
哪些东西会「开一个新堆」
下面这些属性一旦出现在某个元素上,它自己就成了一个独立的层叠上下文:
position: fixed或stickyposition: relative/absolute并且z-index不是autoopacity小于 1(比如为了做淡入淡出设了0.99)transform、filter、perspective不是nonewill-change设了上面那些属性isolation: isolate
我那个下拉菜单的锅,就是祖先上一个为了修 iOS 滚动卡顿加的 transform: translateZ(0) 干的——它默默开了一个新堆,把菜单的高度锁死在里面。
一段能直接粘进控制台的排查脚本
把那个「浮不起来」的元素丢进去,它会帮你列出所有可能开新堆的祖先:
function whyBehind(el) {
const causes = [];
let node = el.parentElement;
while (node) {
const s = getComputedStyle(node);
if ((s.position === "relative" || s.position === "absolute" || s.position === "fixed") && s.zIndex !== "auto") {
causes.push(`position:${s.position} + z-index:${s.zIndex}`);
}
if (s.opacity !== "1") causes.push(`opacity:${s.opacity}`);
if (s.transform !== "none") causes.push(`transform:${s.transform}`);
if (s.filter !== "none") causes.push(`filter:${s.filter}`);
if (s.willChange && s.willChange !== "auto") causes.push(`will-change:${s.willChange}`);
if (s.isolation === "isolate") causes.push("isolation:isolate");
node = node.parentElement;
}
console.log("可能创建了层叠上下文的祖先:", causes);
return causes;
}
// 用法:在元素上右键 → 检查 → 控制台里粘贴上面函数,然后:
// whyBehind($0)
$0 是开发者工具里当前选中的元素,特别好用。
怎么修
最快的办法:给「应该浮在最上面」的那个容器单独开一个干净的堆:
.dropdown {
isolation: isolate; /* 在这个容器内部重新比高低,不受外面干扰 */
}
isolation: isolate 的意思是「我就想自己单开一摊,别让祖先的层级逻辑掺和进来」。比到处加 !important 和疯狂调 z-index 干净多了。
小结
z-index 不是越大越靠前,它只在同一个层叠上下文里比大小。遇到问题,先问一句:「我是不是被某个祖先关进另一个堆里了?」用上面的脚本一查,多半就现形了。
暂无评论,来抢沙发吧~