CSS元素嵌套问题

| 字数 1845

        回顾了一下之前的代码,发现一个css中的坑即层叠顺序。什么叫层叠顺序?简单来说就是在屏幕上某一个位置堆积了很多 div 这些块哪个在上哪个在下的关系就是层叠顺序。(下面代码演示了三层堆叠)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.test2{
position: absolute;
width: 300px;
height: 260px;
top: 50%;
left: 50%;
margin-left: -150px;
margin-top: -130px;
background-color: blue;
}
.test3{
position: absolute;
width: 200px;
height: 200px;
top: 50%;
left: 50%;
margin-left: -100px;
margin-top: -100px;
background-color: black;
}
.test4{
position: absolute;
width: 150px;
height: 150px;
top: 50%;
left: 50%;
margin-left: -75px;
margin-top: -75px;
background-color: gray;
}

对应的html结构如下:

1
2
3
4
5
<div class="test2">
<div class="test3">
<div class="test4"></div>
</div>
</div>

如图所示,都是水平垂直居中的。这里我们主要关注层叠顺序。此时三者均为绝对定位,由test2开始从下到上依次是 test2 -> test3 -> test4 这样的顺序。test4位于最上方,此时想让test3与test4位置颠倒且test2位置不变(由下到上的顺序为test2 -> test4 -> test3),应该怎么做?
肯定首先想到使用 z-index:

1
2
3
4
5
6
7
8
9
.test3 {
//...
z-index: 3
}

.test4 {
//..
z-index: 2
}

这样修改结果并不会改变。为什么?既然 z-index 不生效那我们肯定先去查 z-index 。

对于一个已经定位的元素(即position属性值是非static的元素),z-index 属性指定:
元素在当前堆叠上下文中的堆叠层级。
元素是否创建一个新的本地层叠上下文。

MDN : z-index

根据mdn的介绍来看,我们设定了堆叠层级,这样来说是应该可以的。但是结果却不尽人意。留意一下层叠上下文,是否跟创建的层叠上下文有关呢?
常见的创建层叠上下文(stacking context)的方式有:

  • z-index 值不为 “auto”的 绝对/相对定位

  • 一个 z-index 值不为 “auto”的 flex 项目 (flex item),即:父元素 display: flex|inline-flex

可能你并没有意识,但你确实会常常这样为html标签创建样式并为它创建了层叠上下文。

层叠上下文的特点:(mdn)

  1. 给一个 HTML 元素定位和 z-index 赋值创建一个层叠上下文,(opacity 值不为 1 的也是相同)。
  2. 层叠上下文可以包含在其他层叠上下文中,并且一起创建一个有层级的层叠上下文。
  3. 每个层叠上下文完全独立于它的兄弟元素:当处理层叠时只考虑子元素。
  4. 每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会 在父层叠上下文中 按顺序进行层叠。

具体的层叠关系如下图:

深入理解CSS中的层叠上下文和层叠顺序

对于当前代码来说,我创建了三个层叠上下文元素并且一一嵌套。当我们设置test3的z-index时,test4是test3的子元素(层叠上下文特点3),会影响到test4。此时test3与test4一同来到了test2上方(其实也就是保持了原位置,因为test2没有设置zindex所以没有层叠上下文)。
这个时候再设置test4的z-index,相当于俩个层叠上下文元素在比较z-index,那按照上图所示的比较规则,test3的z-index只要大于test4就能在上面了,那为什么还是不变呢?我们来看看大神的解释:

层叠上下文元素有如下特性:(理解元素层叠上下文-张鑫旭

1、层叠上下文的层叠水平要比普通元素高(原因后面会说明);

2、层叠上下文可以阻断元素的混合模式;

3、层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。

4、每个层叠上下文和兄弟元素独立,也就是当进行层叠变化或渲染的时候,只需要考虑后代元素。

5、每个层叠上下文是自成体系的,当元素发生层叠的时候,整个元素被认为是在父层叠上下文的层叠顺序中。

根据上面第3点我们知道了内部层叠受制于外部层叠,结合代码也就是说只要test3设置了z-index,那它就会带着test4一起走。当test4设置z-index时,首先要判断设置的值和test3的z-index正负是否相同,如果都为正(同为负),无论大小test3与test4的位置永不会变。既然设置俩个正值作为z-index行不通,那我们试试一正一负行得通吗?

        首先设置test4的z-index为-1,此时只有一个层叠上下文test4,并且层叠顺序为:由test4开始从下到上依次是 test4 -> test2 -> test3 这样的顺序。test3位于最上方,此时如果给test3的z-index赋正值会导致test4重回原位置(test3上方),赋负值会导致test3来到了test2之下,不符合要求。既然父级元素没有操作空间了,试试父父级元素test2,根据特点3我们猜想如果test2的z-index设置正值那么会把test3(包括test4)这个整体共同带到test2之上的位置。如下:

test.png

在改变test4的z-index之后(为了看得更清晰我手动改变了长宽)我们发现达到了效果。设置父父级(test2)的z-index为1之后拥有了层叠上下文,test4设置z-index为-1也拥有了层叠上下文。此时test3作为普通的绝对定位元素保留在原位置(为了方便理解所以这样描述,这里test3其实是bfc发挥了作用),因为test2是父父级符合特点3,所以将位于底下的test4带了上来,但是test4属于test3的子元素,上来后就去和test3比较,因为test3不拥有层叠上下文保留在原位置,而此时test4的z-index为负值,所以在test3之下,test2之上显示

      这里说了三个块级元素,大家还可以试试嵌套行内元素是什么效果。需要注意的是伪元素创建层叠上下文的表现是否和上面一样呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.test{
position: absolute;
width: 300px;
height: 260px;
top: 50%;
left: 50%;
margin-left: -150px;
margin-top: -130px;
background-color: blue;
z-index: 1;
}
.test1 {
position: absolute;
width: 200px;
height: 200px;
top: 50%;
left: 50%;
margin-left: -100px;
margin-top: -100px;
background-color: black;
}
.test1:after{
content: '';
position: absolute;
width: 100%;
height: 100%;
background-color: gray;
animation: breathe infinite ease-in-out 2s;
opacity: 0;
z-index: -1;
}
@keyframes breathe{
0% {
transform:scale(.5);
opacity:.9;
}
95% {
transform:scale(1.5);
opacity:0;
}
100% {
transform:scale(.9);
opacity:0;
}
}

html如下:

1
2
3
<div class="test">
<div class="test1"></div>
</div>

这个其实就是我遇到的问题,用伪元素创建了层叠上下文,我想让伪元素动画出现在test1的下方test的上方。和上面一样分别设置z-index为-1和1即可达到效果。

参考资料: