z-index生效的前提是元素position不能为static;它只在层叠上下文中起作用,且需避免父级opacity、transform等意外创建新层叠上下文;推荐用CSS自定义属性分档管理层级。
给一个 div 写 z-index: 999 却没用?先看它的 position 值是不是还停留在默认的 static。这是最硬性的门槛——z-index 对 static 元素完全不响应,连计算都不会参与。
必须显式设为以下之一:
position: relative(适合微调或作为子元素定位上下文)position: absolute(脱离文档流,依赖最近已定位祖先)position: fixed(相对于视口,常用于弹窗、导航栏)position: sticky(滚动时切换行为,需配合 top/bottom)漏掉这一步,后面所有数值都是白写。
z-index 不是全局排名表,而是一张张“局部排行榜”。两个元素即使 z-index 相差 1000,只要分属不同层叠上下文,就互不比较。
哪些操作会意外创建新层叠上下文?常见但容易被忽略的有:
opacity: 0.99(哪怕只是 0.99,不是 1)transform: translateX(0) 或任何非 none 值filter: blur(1px)、will-change: transform
position: fixed 且 z-index 不为 auto
浏览器 DevTools 的 “Computed” 面板里,如果看到目标元素的 z-index 显示为灰色或标着 auto,往上逐级点开父节点,重点查这些触发属性。
靠临时堆高 z-index: 999999 解决遮挡,等于给后续维护埋雷。真正稳定的规则是分档、留空、可预期。
推荐在根级定义几档基础层级:
--z-base: 0(普通内容、卡片主体)--z-nav: 100(顶部导航、固定侧边栏)--z-dropdown: 200(下拉菜单、Tooltip)--z-modal-overlay: 1000(半透明遮罩层)--z-modal-content: 1001(弹窗本体)--z-toast: 2000(全局提示,需高于弹窗)然后组件内直接引用:z-index: var(--z-modal-content);。这样改一处,全链路生效,也避免不同团队各自乱用 999/9999。
点击不到底层元素,往往不是它自己层级低,而是它被锁在一个低优先级的层叠上下文里出不来。比如:
.modal 被包裹在 .page-container 中,而后者设置了 transform: scale(1) → 整个 modal 及其子元素都在这个新上下文里,再高的 z-index 也超不过同级的 .header
opacity: 0.999,导致里面的按钮永远点不到此时强行调高子元素 z-index 无效。解法只有两个:
opacity: 0.999 改成 rgba() 背景色)position: fixed 挂到 body 下,再用 JS 同步位置(慎用,影响可访问性和 SSR)复杂嵌套中,层叠上下文比 z-index 数值更关键——它决定了你的数值有没有资格参与比较。