z-index 为什么有时不生效层叠上下文是怎么决定遮挡顺序的z-index 要放在层叠上下文里理解。同一上下文中的元素按相应绘制规则比较子上下文作为整体参与父级排序内部子元素不能靠更大的 z-index 越过这个整体的层级。例如弹窗在 z-index 1 的父上下文里导航栏处于同级 z-index 2 的上下文。弹窗内部即使是 999999也可能仍在导航栏下面。排查时我会沿祖先找 position 与 z-index、transform、opacity 等建立上下文的条件再检查 overflow 裁剪。修复可以是调整正确的父级层级或把弹层挂到合适的根容器必要时使用浏览器 top layer而不是继续堆数值。先找到排序的层再比较数字把 999999 放回它所在的上下文假设页面有两个兄弟区域内容区建立层叠上下文z-index 为 1导航栏也建立层叠上下文z-index 为 2。弹窗放在内容区里面自己的 z-index 为 999999。浏览器在它们共同的父上下文中比较时先比较的是内容区与导航栏这两个整体。内容区排在导航栏下面里面的弹窗也就一起在下面。弹窗的巨大数值只能帮助它在内容区内部排序。可以把它理解成先比较目录再比较目录里的条目但不要把这个比喻当成浏览器真的在计算“1.999999”这样的数值。关键是上下文内部排序完成以后会作为整体参与外部排序。哪些设置会悄悄建立新上下文常见条件包括定位元素配合非 auto 的 z-indexfixed 或 sticky 元素opacity 小于 1非 none 的 transform以及 isolation isolate 等。Flex 和 Grid 子项使用非 auto 的 z-index 也可以建立上下文。所以“z-index 必须配合 position 才有效”也不够准确它遗漏了 Flex/Grid 子项等情况。这里需要分清层叠上下文、包含块和合成层。它们可能被某些相同属性影响但不是同一个概念。给祖先加 transform除了改变层叠关系还可能让 fixed 元素相对这个祖先定位不能看到 GPU 图层就认为 z-index 问题已经解决。遮挡和裁剪要分开排查弹窗还可能被祖先的 overflow 裁剪。增大层级不能让它自动逃出所有裁剪关系。因此可以按这个顺序检查先确认元素有没有渲染、尺寸与位置是否正确再找遮住它的元素和共同上下文最后检查祖先的 overflow、clip-path 等。开发者工具里暂时取消一个 transform 或 overflow很适合用来定位原因但不能把“删掉一个属性后正常”直接当成最终方案。这个属性可能承担动画、滚动或布局职责。弹层应该放在哪儿如果弹窗要覆盖整个页面把它通过框架 Portal 或 Teleport 挂到统一的弹层根容器通常比放在局部卡片内部更好管理。这里讨论放置原则React Portal 和 Vue Teleport 各有自己的实现不存在通用 Python 浏览器 API。不过换了 DOM 放置位置还要检查焦点、滚动锁定和无障碍关系。弹窗能盖住导航栏只说明视觉层级修好了不代表交互已经完整。原生 dialog 的模态显示、Popover 等机制可以进入浏览器 top layer。它不参与普通内容的同一套 z-index 排名但仍要遵守对应 API 的生命周期和交互语义。本题机制参考MDNStacking context。面试官继续追问z-index 越大浏览器越慢吗数值大本身不是主要性能问题。更值得检查的是复杂绘制、图层数量和动画方式这里首先是排序和维护问题。opacity 0 会让元素不占空间吗不会。它仍可能占布局空间并参与交互。是否显示、是否占空间、是否响应事件是不同问题。Portal 能自动解决所有弹窗问题吗不能。它能改变 DOM 放置位置避开部分祖先约束但焦点管理、滚动、遮罩和层级约定仍要自己设计。面试速记卡z-index在对应层叠上下文中比较不是全页面统一排名。子上下文作为整体参与父级排序内部大数值不能越级。常见触发定位与 z-index、transform、opacity、isolation。排查遮挡、定位和裁剪分别检查。弹层统一根容器或合适的 top layer仍需处理交互语义。我是程序员Sunday。本文收录于 sunday面试指南原文与配图可继续阅读。