先记住这个答案
isolation: isolate 的作用是让元素创建一个新的层叠上下文,也就是一个独立的「图层组」。组里元素的 z-index 只在组内比较,整组作为一个整体再和外面的元素比。它最常见的两个用途是:把组件内部的 z-index 圈起来,不和页面上的弹窗、导航抢层级;限制 mix-blend-mode,让混合只发生在组件内部。和 transform、opacity、will-change 这些也能创建层叠上下文的属性相比,它的优点是没有任何副作用,不改变包含块,不改变外观,也不强制提升合成层。
isolation: isolate创建层叠上下文- 常用来把组件内部的
z-index圈在组件里 - 它还可隔离组件内混合效果
- 变换祖先会影响固定定位包含块
用途一:把组件内部的 z-index 圈起来
组件里有个角标,为了盖住卡片图片写了 z-index: 10。页面上的吸顶导航是 z-index: 5,滚动时角标就从导航上面穿了过去。角标只需要压住自己卡片里的图片,却和整个页面比了层级。
isolation: isolateCSS.header {
position: sticky;
top: 0;
z-index: 5; /* 吸顶导航 */
}
.card {
isolation: isolate; /* 卡片自成一个层叠上下文 */
}
.card .badge {
position: absolute;
z-index: 10; /* 只在 .card 内部和图片比较 */
}
/* 没加 isolation:badge 的 10 和 header 的 5 比,badge 穿到导航上面
加了之后:.card 整体按层级 auto 参与外部比较,排在 header 下面 */加了 isolation: isolate 后,.badge 的 z-index: 10 只在 .card 内部有效。.card 自己在外面没有设置 z-index,按普通层级参与排序,自然在 z-index: 5 的导航下面。组件作者不用知道页面上其他地方用了多大的 z-index,这就是「隔离」的意思。
- 设计系统里的用法
- 一些组件库会在组件根元素上默认加
isolation: isolate,这样组件内部可以放心用 1、2、3 这种小数字排层级,不会因为页面上别的地方写了z-index: 9999而出问题。这也是解决「z-index越写越大」的一个办法:层级只在各自的层叠上下文内比较,数字就不用无限往上加。
用途二:限制 mix-blend-mode 的混合范围
isolation 这个属性最初就是为混合模式设计的。mix-blend-mode 会让元素和它背后所有已经画好的内容混合,一直混到最近的层叠上下文为止。
- 只和组件自己的背景混合
- 比如一个标题设了
mix-blend-mode: multiply,本意是和卡片的背景色混合,结果把页面背景图也混了进去,颜色完全不对。给卡片加上isolation: isolate,卡片就成了混合的边界,标题只和卡片内部的内容混合,卡片外面的东西不参与。
为什么不用 transform 来创建
以前没有 isolation 或者大家不熟悉它时,常见的写法是 transform: translateZ(0)、opacity: 0.99 或者 position: relative 加 z-index: 0。它们都能创建层叠上下文,但各自带着额外的效果。
transform的副作用transform不为none的元素会成为所有position: fixed后代的包含块。组件里如果有一个fixed定位的弹窗或遮罩,本来应该铺满视口,加了transform后就只铺满这个组件,而且会跟着组件一起滚动。这是实际项目里最常踩的坑。另外translateZ(0)常常会让浏览器把元素提升成单独的合成层,数量多了会占用显存。元素本来就有transform动画时,写死的translateZ(0)还可能被动画覆盖掉。- 其他写法的问题
opacity: 0.99会让元素有一点点透明,截图对比能看出来。position: relative加z-index: 0相对干净,但它改了position,会让元素成为absolute后代的包含块,原来相对更外层定位的子元素位置就变了。isolation: isolate不改变任何这些,所以能放心加在任何元素上。
容易答错的地方
- 以为加了
isolation元素就会浮到最上面 isolation不改变元素自己在外部的层级。它只是把内部的层级「封装」起来,外部比较时整个组仍然按元素自己的z-index和文档顺序排。想让组件整体压在别人上面,还是要给组件本身设z-index。- 以为
isolation会影响子元素的定位 - 它不会成为
absolute或fixed后代的包含块,子元素的top、left参照谁,和加不加isolation没有关系。这正是它比transform好用的地方。 - 以为
isolation: auto表示不创建上下文 auto是默认值,意思是不因为isolation属性而创建层叠上下文。如果元素同时有opacity: 0.5或transform,照样会创建。auto不能「取消」其他属性带来的层叠上下文。
面试官还会怎么问?
除了 isolation,还有哪些属性会创建层叠上下文?
常见的有:根元素 html;position 为 absolute 或 relative 且 z-index 不是 auto;position: fixed 或 sticky;Flex 或 Grid 子项设了 z-index;opacity 小于 1;transform、filter、perspective 不为 none;mix-blend-mode 不为 normal;will-change 指定了上述属性;contain 为 layout 或 paint。
怎么排查一个元素为什么盖不住另一个?
先找出两个元素各自所在的层叠上下文,一路往上找祖先,看哪个祖先创建了上下文。如果它们不在同一个上下文里,比较的是两者最近的、处在同一上下文中的祖先的层级,子元素自己的 z-index 写多大都没用。Chrome 开发者工具的 Layers 面板和一些浏览器扩展能直观看到层叠上下文。
isolation 的兼容性怎么样?
现代浏览器都支持,IE 不支持。要兼容 IE 的老项目只能退回用 position: relative 加 z-index: 0,并留意它对 absolute 子元素定位参照的影响。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。