理解层叠上下文与元素层叠顺序
在没有 CSS 布局的年代里,web 网页都是按照元素类型(块级元素或是行内元素等等),自上而下、从左至右的顺序排列,两两之间互不重叠。原理足够简单,但是无法实现元素的层叠效果。在有了 CSS 布局之后,float、position 来了丰富的表现能力,但是同样带来了复杂的表现效果。CSS 标准中引入了层叠上下文(The stacking context)的概念,来帮助我们理解 HTML 元素发生层叠时的复杂表现。
层叠上下文:
如果把我们的显示器屏幕看作一个没有厚度的二维平面,我们的视线垂直穿过这个平面,产生了一条贯穿平面的轴线,这条轴线就是 Z 轴。显示器的屏幕所在那个二维平面,就称为一个层叠上下文(The stacking context)。层叠上下文是一个容器,用来存放 HTML 元素和新的层叠上下文。我们用 Z 轴距离来描述层叠上下文与 HTML 的堆叠顺序,所以 Z 轴距离是没有单位的无量纲量,没有物理上的距离意义。于此同时,每个层叠上下文都会创建一个新的 Z 轴。
简而言之,就是**没有创建自己的层叠上下文的元素,将被父层叠上下文包含。子层叠上下文,也会被父层叠上下文包含。**所有的元素和层叠上下文,都被根元素(HTML)的层叠上下文包含。
常用的创建层叠上下文的方式:
- 根元素 (HTML)
- z-index 值不为 “auto”的 绝对/相对定位
- z-index 值不为 “auto”的 flex item
- opacity 属性值不为 1 的元素
- transform 属性值不为 “none”的元素
- position: fixed
层叠顺序:
理解了层叠上下文的概念之后,就可以探讨层叠顺序了。 先来看看元素处在同一层叠上下文内的表现:
<style>
.demo {
position: relative;
padding-top: 15px;
width: 100%;
height: 350px;
background: #eee;
border: 1px solid #eee;
font-size: 12px;
color: #333;
}
.demo div {
border: 1px dashed #000;
}
.demo-0 .float {
margin: 10px;
width: 12.5%;
height: 300px;
background-color: #ffc;
}
.demo-0 .left {
float: left;
}
.demo-0 .right {
float: right;
}
.demo-0 .normal {
margin-top: 1em;
height: 100px;
background-color: #cff;
}
.demo-0 .normal + .normal {
margin: -80px auto;
width: 25%;
height: 60px;
background-color: #ccf;
}
.demo-0 .absolute {
position: absolute;
top: 115px;
left: 6.25%;
width: 62.5%;
height: 150px;
background-color: #fcf;
}
.demo-0 .relative {
position: absolute;
top: 125px;
right: 6.25%;
width: 62.5%;
height: 150px;
background-color: #ccf;
}
</style>
<div class="demo demo-0">
<div class="absolute">div0:绝对定位</div>
div0 ~ div5表示元素在HTML文档中出现的顺序。
<div class="float left">div1:向左浮动</div>
<div class="float right">div2:向右浮动</div>
<div class="normal">div3:普通流</div>
<div class="normal">div4:普通流</div>
<div class="relative">div5:相对定位</div>
</div>
通过上面的 demo,我们可以得到如下层叠顺序(从底到顶顺序):
- 根元素的背景与边框
- 普通流里的块元素,按 HTML 中的出现顺序堆叠
- 浮动块元素
- 普通流里的后代行内元素
- 定位元素按 HTML 中的出现顺序堆叠
了解了同一层叠上下文中的层叠顺序之后,我们就可以探讨一下层叠上下文互相之间的层叠顺序。我们先来看这样一个 demo:
<style>
.demo-1 {
z-index: 0;
}
.demo-1 .z-1 {
position: absolute;
z-index: -1;
top: 10px;
right: 10px;
bottom: 10px;
left: 10px;
background-color: #fcc;
}
.demo-1 .z1 {
position: absolute;
z-index: 1;
top: 50px;
right: 50%;
bottom: 50px;
left: 6.25%;
background-color: rgba(255, 255, 204, 0.4);
}
.demo-1 .z0 {
position: absolute;
z-index: 0;
top: 75px;
right: 6.25%;
bottom: 75px;
left: 18.75%;
background-color: #cfc;
}
.demo-1 .z100 {
position: absolute;
z-index: 100;
top: 75px;
right: 6.25%;
bottom: 75px;
left: 18.75%;
background-color: #ccf;
}
.demo-1 .normal {
margin-top: 150px;
width: 37.5%;
height: 100px;
background-color: #fff;
}
.demo-1 .opacity {
margin: 200px 0 -300px 12.5%;
width: 12.5%;
height: 100px;
background-color: #ccf;
opacity: 0.9;
}
</style>
<div class="demo demo-1">
<div class="z1">div0: z-index = 1</div>
<div class="z0">
div1: z-index = 0
<div class="z100">div1.1: z-index = 100</div>
</div>
<div class="opacity">div2: opacity: 0.9</div>
<div class="normal">div3:普通流</div>
<div class="z-1">div4: z-index= -1</div>
</div>
通过上面的 demo,我们可以得到如下层叠顺序(从底到顶顺序):
- 层叠上下文的背景与边框
- z-index < 0
- 普通流里的块元素
- z-index = 0 与 非 z-index 创建的层叠上下文
- z-index > 0
结合了层叠上下文内与层叠上下文间的层叠顺序之后,我们可以整理出这样的层叠顺序(从底到顶顺序):
- 层叠上下文的背景与边框
- z-index < 0
- 普通流里的块元素,按 HTML 中的出现顺序堆叠
- 浮动块元素
- 普通流里的后代行内元素
- 定位元素、z-index = 0、非 z-index 创建的层叠上下文,按 HTML 中的出现顺序堆叠
- z-index > 0
结语:
在大多数时候(没有创建新的层叠上下文的时候),即使没有掌握 CSS 层叠上下文以及元素层叠顺序,z-index 的表现也能符合预期。我想,这大概就是 CSS 的易学难精的原因:即使你没有掌握它的原理,凭借直觉你也能够在某种程度上使用它。因此,人的惰性会让自己本能的逃避 CSS 复杂的布局原理。不知道我能不能坚持把 CSS 布局原理理解透彻呢?行远自迩,登高自卑。以此自勉!