前言
好的东西就要拿出来分享,分享使人进步。最近抽空把这块知识点给吃透了,然后赶紧整理成材料分享给大家,希望能帮到所有看到的人~
1 理论规范
为什么浏览器渲染出来的效果和我预期的不一样呢?浏览器也是按照一定规范去实现对应的渲染规则的,为了更好的理解这块的渲染规则,我把CSS2.1规范中关于z-index这块进行了翻译 CSS2.1规范z-index部分,感兴趣的同学可以多驻足停留一下。当然,这个不是必须的,下面我会用更通俗易懂的方式进行讲解。
2 理解概念
2.1 层叠上下文(stacking context)与Z轴



划重点:
- 层叠上下文在一个HTML文档树中是可以有多个的,而引发创建层叠上下文的盒模型元素,我们称之为层叠上下文元素;
- html根元素是一个默认的层叠上下文元素;
- 给定位元素设置z-index属性,且属性值为一个整数,该元素也会成为一个层叠上下文元素。
2.2 层叠水平(stack levels)与z-index
为什么把层叠水平和z-index放在一起说明呢,因为这两个其实是紧密相连的。层叠水平可以按照字面上来进行理解,由于在层叠上下文中,盒模型元素之间可能会出现视觉上的重叠,层叠水平就是用来描述当重叠出现的时候,谁叠在上面,谁叠在下面的一个水平层级概念。而z-index就是用来指定层叠水平的具体数值的。
划重点:
- z-index的具体数值,就是指代的在当前层叠上下文里,该元素的具体层叠水平;
- 你可以通过z-index去尝试指定层叠水平,但需要注意,只有对定位元素(除static之外的定位)使用才会生效;
- 当指定z-index的值是一个具体整数的时候,并且是定位元素(除static之外的定位),那么会在该元素创建一个新的层叠上下文。
2.3 层叠顺序(stacking order)
既然层叠水平已经是用来描述层叠谁上谁下的,那么为什么还要有一个层叠顺序呢,他们之间又是一个什么关系呢?层叠顺序可以理解为进行层叠比较或者渲染的一个具体规则。但是这个规则比较复杂,为了方便理解,可以由叠放的上下顺序大致地划分出若干等级来,而这个等级就是层叠水平。具体的层叠顺序如下图所示:

2.3.1 顺序解读(越后面的层叠顺序越大):
- 导致创建层叠上下文的元素的背景、边框;
- 层叠上下文的子层叠上下文元素,并且z-index设置的是负值;
- 简单理解为块元素,但有些附加条件;
- 简单理解为浮动元素,但有些附加条件;
- 简单理解为行元素,但有些附加条件;
- 这个比较抽象,在2.3.2将进行详细讲解;
- 层叠上下文的子层叠上下文元素,并且z-index设置的是正值。
2.3.2 层叠水平为0:
层叠水平为0的元素,有两种情况会导致出现层叠水平为0的元素。
- 定位元素(除static之外的定位)设置z-index为0;
- 普通元素通过设置了某些css属性导致层叠水平自动提升为0。以下情况会导致层叠水平自动提升到0:
- opacity值不为1的元素
- transform值不为none的元素
- 设置了z-index为具体数字数值,且父元素为flex的元素
- 使用了css3 filter的元素
- 使用了will-change进行加速的元素
- mix-blend-mode值不为normal的元素
- isolation值为isolate的元素
- overflow或者overflow子属性的值有一个为scroll、auto的元素(IOS 移动端)
3 方法论
上面规则太复杂?我整理了一个简约版,囊括了大部分要点,可以胜任绝大部分场景(如需KO所有场景,还是需要严格按2.3的层叠顺序来进行比较)。
3.1 比较方法简约版
- 正z-index层叠上下文元素 > 层叠水平为0的元素 > 普通元素
- zIndex大则大(层叠上下文元素间比较适用)
- 后来居上原则,文档中后面出现者大
划重点:
- 序号数字代表优先使用的比较条件,只有当双方条件不分胜负,那么再套用下一个条件;
- 层叠上下文可以嵌套,所有子元素(普通元素、层叠上下文元素)显示层级均受制于所属层叠上下文;
- 两个进行比较的元素所处的最近的一个层叠上下文,必须是同一个层叠上下文,才能直接进行比较。反之则需要向上(父元素)逐层寻找层叠上下文元素,直到满足直接比较条件为止。
4 实战论证
4.1 论证7级层叠顺序
代码:
效果:

说明:
为了方便验证,增强可读性,同时减少其他因素干扰。html采用结构简单,最外层一个父元素.box-wrap,里面直接排列7个子元素。可以看到如下结果:
- 父元素是一个定位元素,并且设置了z-index=0,导致这个元素变成了层叠上下文元素。但依据7级层叠顺序,所以它显示在了最底层;
- 第7个子元素.sub-box7,它在文档流中是最后出现,但是它是一个定位元素,同时设置了z-index是一个负值,所以它显示在了倒数第二层;
- 第6个子元素.sub-box6,它是一个普通块元素,也没有设置其他影响排布的css属性,所以它显示在了倒数第三层;
- 第5个子元素.sub-box5,它是一个浮动元素,也没有设置定位,所以它显示在了倒数第四层;
- 第4、第3、第2这三个子元素放在一起说明,因为它们3个在7级层叠顺序里都属于“层叠水平为0元素”。.sub-4它是一个定位元素并且设置z-index:0,所以它层叠水平是0。.sub-3由于设置了opacity不为0,导致获得层叠水平提升至0。.sub-2由于设置了transform不为auto,导致获得层叠水平提升至0。而最终呈现层级效果是4>3>2,因为层叠水平一致,所以适用后来居上原则,在文档中后面出现的层叠顺序大;
- 第1个子元素.sub-box1,它是一个定位元素,并且设置了z-index为正数,导致它处于7级层叠顺序里的最高级别,所以哪怕它是第1个子元素,在文档中子元素里最先出现的,实际渲染依然会展示在最上面。
划重点:
- 第4、第3、第2这三个子元素大家可以自己在demo中调整文档出现先后顺序,就可以看到它们的实际渲染顺序大小也会跟着变。从而验证了它们都是“层叠水平为0元素”这一点。
- 尝试给第2、第3个子元素设置z-index为一个正整数,可以看到不会影响到实际渲染层级。从而论证z-index只对定位元素有效,并且通过设置css某些属性导致的层叠水平提升到0,也仅仅只是提升了层叠水平,它本身依然不是一个真正的层叠上下文元素。
4.2 论证非兄弟元素的比较
代码:
效果:

说明:
简单介绍一下html结构。如同元素内容所示,A、B、C3个元素互为兄弟节点。而A元素下有一个子元素即为A-1,同时A-1下面又有一个子元素,即为A-1-1。B-1、B-2可以依次类推。此番用于比较优先级的两个元素分别是A-1-1和B-1。为了加深大家的理解,这回在最右边准备了4个按钮,可以用来分别给指定元素设定z-index。
- 初始状态:A-1-1和B-1两元素所属的最近层叠上下文都是html根元素这个层叠上下文,所以这两个元素可以直接进行比较。它两都是定位元素,并且也都没有设置z-index,所以层叠水平都是0。此时后来居上原则,所以B-1的层叠优先级大于A-1-1;
- 点击右侧第1个按钮:A-1-1作为一个定位元素设置了z-index为一个正整数,正层叠上下文元素大于层叠水平0元素,所以导致渲染结果A-1-1叠在了B-1上面;
- 点击右侧第2个按钮:B-1作为一个定位元素也设置了z-index为一个正整数,此时两个层叠上下文元素相比,z-index也相同,那么自然后面出现的优先级高,所以B-1又重新叠在了A-1-1上面;
- 点击右侧第3个按钮:此时A-1-1的z-index大于B-1,两层叠上下文元素比较,z-index大者层叠优先级大,所以A-1-1又重新叠在了B-1上面;
- 点击右侧第4个按钮:A-1是一个定位元素,由于设置了z-index为一个整数,导致A-1变成一个层叠上下文元素。此时A-1-1就不能直接和B-1进行比较了,因为A-1-1此时所属的最近一个层叠上下文变成了A-1。所以A-1-1与B-1的比较,就变成了A-1与B-1进行比较。层叠上下文元素比较,z-index大者大,所以B-1会叠在A-1上面。子元素的层叠顺序受制于所属层叠上下文的层叠顺序,所以B-1自然也是叠在A-1-1上面。
结尾
好了,这次分享到这里为止,大家有任何疑问或者问题,都可以在评论区给我留言,我会尽我所能来为大家解答的~