z-index 控制重叠元素的前后堆叠顺序——较高的值会显示在上面。但它只适用于定位元素,并且它在堆叠上下文内运行,这是大多数 z-index 混淆的根源。
css
{ : fixed; : ; }
{ : fixed; : ; }
{ : relative; : ; }
z-index 在 position: static(默认值)上被忽略——元素必须是 relative、absolute、fixed 或 sticky,z-index 才能生效。
堆叠上下文是一个独立的"层"。z-index 值只在同一上下文内竞争——子元素无论 z-index 多高,都无法逃离其父元素的堆叠级别。
.parent-a { position: relative; z-index: 1; }
.parent-b { position: relative; z-index: 2; }
.child-of-a { position: absolute; z-index: 9999; }
/* ❌ child-of-a STILL renders below everything in parent-b, */
/* because parent-a (z-index 1) is entirely below parent-b (z-index 2). */
这是经典的 "我的 z-index: 9999 模态框仍然在某个元素后面" 的错误——模态框被困在一个处于较低层的父元素内。
许多属性会悄悄创建一个,这可能会困住后代元素:
- position + z-index (other than auto)
- opacity less than 1
- transform, filter, will-change
- isolation: isolate
- a flex/grid item with z-index
/* render the modal at the document root (outside trapping parents) — "portal" */
.isolated { isolation: isolate; } /* deliberately create a context where you want one */
在实践中,在 DOM 的顶部渲染模态框/提示框(React 传送门),这样它们就不会被困在低层的父元素内。
堆叠上下文解释了最令人困惑的 z-index 错误:一个很高的 z-index 仍然输给了别的元素,因为它的祖先处于更低的层。
了解 opacity、transform 和定位后的 z-index 会创建上下文——以及子元素无法逃离它们——让你能够调试覆盖层/下拉菜单的排序问题,并正确地组织你的 DOM(传送门)。