一,前言
今天遇到一个布局兼容问题,调试了一番,发现z-index的表现和自己的认知不相符,才知道自己对z-index的认知有错误,于是写篇文章总结下这个z-index的具体使用。有基础的朋友可以直接看第四节。
二,标准文档流
在了解z-index之前,需要先知道标准文档流。
标准文档流其实是浏览器渲染页面的一个规则:所有的页面元素,都要按照它在HTML文档中出现的先后顺序,依次在浏览器中,从左上角开始,从上到下,从左到右的顺序依次显示。
具体的表现为:
2.1,块级元素:
有以下的几个特征:
1.单独的占一行
2.可以设置宽、高
3.如果不设置宽度,他将默认占满父盒子的宽度
2.2,行内元素:
1.与其他元素自动的并排在一行上
2.不能设置它的宽和高,它的宽和高就是内容的宽和高
2.3,块级元素和行内元素可以相互转换:
display:block
display:inline
三,position
常见的使元素脱离文档流的手段有:浮动和定位。
之所以讲这个,是因为只有脱离了文档流,才有z-index来设置元素的层叠等级。
浮动好理解,定位主要是position的几个属性值:
通常情况下, position
有以下几个取值。具体如下:
取值 | 含义 | 说明 |
---|---|---|
static | 静态定位 | 对象遵循标准文档流,top,right,bottom,left 等属性失效。 |
relative | 相对定位 | 对象遵循标准文档流中,依赖top,right,bottom,left 等属性相对于该对象在标准文档流中的位置进行偏移,同时可通过 z-index 定义层叠关系。 |
absolute | 绝对定位 | 对象脱离标准文档流,使用 top,right,bottom,left 等属性进行绝对定位(相对于 static 定位以外的第一个父元素进行绝对定位) ,以及可通过 z-index 定义层叠关系。 |
fixed | 固定定位 | 对象脱离标准文档流,使用 top,right,bottom,left 等属性进行绝对定位(相对于浏览器窗口进行绝对定位)同时可通过 z-index 定义层叠关系。 |
sticky | 粘性定位 | 可以说是相对定位 relative 和固定定位 fixed 的结合。元素固定的相对偏移是相对于离它最近的具有滚动框的祖先元素,如果祖先元素都不可以滚动,那么是相对于 viewport 来计算元素的偏移量。 |
这些内容,网上很多文章都有,不再赘述,本文主要讲解z-index的规则,不忘初心。
四,z-index的层叠上下文理解
z-index应该这样理解:每一个层叠上下文就是一层楼,而该楼层的z-index就是楼梯。1楼层的楼梯值再大,也不可能比2层楼高。
于是我们首先要理解一个层叠上下文的生成条件:
1,根元素html ;
2,绝对定位 absolute 或相对定位 relative 且 z-index 值不为 auto ;
3,一个 flex 项目,且 z-index 值不为 auto ,也就是父元素 display: flex|inline-flex ;
4,元素的 opacity 属性值小于 1 ;
5,元素的 transform 属性值不为 none ;
6,元素的 mix-blend-mode 属性值不为 normal ;
7,元素的 isolation 属性被设置为 isolate ;
8,在 mobile WebKit 和 Chrome 22+ 内核的浏览器中,position: fixed 时总是会创建一个新的层叠上下文, 即使 z-index 的值是 auto ;
元素的 -webkit-overflow-scrolling 属性被设置 touch 。
后面几个比较少用暂且不谈,本文主要讲前几个。需要理清几个名词概念:
层叠上下文:对应本节中所说:生成的一个层叠上下文(一个楼层)
层叠等级:一个层叠上下文中的元素层叠规则:七层层叠规则
层叠上下文层级;每个层叠上下文之间是有级别的,低级别的无论z-index多大都无法突破高级别的层叠上下文。各个层叠上下文之间,用层级来描述(有的文章还是用层叠等级,这样容易混乱,所以我换种说法)
五,z-index的层叠等级
理解了层叠上下文还不够,因为每层层级上下文内还分为七个层叠等级。具体如下图:
如下代码,就能体现出这七层结构:
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
height: 100vh;
background: pink;
}
/* 新创建一个层叠上下文 */
.box{
height: 350px;
background: greenyellow;
position: relative;
z-index:1;
}
.total{
height: 100px;
width: 300px;
text-align: center;
position: absolute;
}
.first-item{
background: yellow;
top: 10px;
left:10px;
z-index: -1;
}
.second-item{
background: blueviolet;
display: block;
top: 40px;
left:40px;
}
.third-item{
background: red;
float: left;
top: 70px;
left:70px;
}
.fourth-item{
background: blue;
display: inline;
top: 100px;
left:100px;
}
.five-item{
background: rgb(18, 248, 217);
top: 130px;
left:130px;
z-index: auto;
}
.six-item{
background: rgb(248, 83, 18);
top: 160px;
left:160px;
z-index: 1;
}
</style>
<div class="box">
<div class="first-item total">z-index小于0 </div>
<div class="second-item total">block块级盒子</div>
<div class="third-item total">float浮动盒子</div>
<div class="fourth-item total">inline/inline-block水平盒子</div>
<div class="five-item total">z-index:auto或0或不设置</div>
<div class="six-item total">z-index大于0</div>
</div>
</body>
</html>
实现的效果和七层结构如出一辙:
如上文第四节创建层叠上下文的知识可知,这里html默认会创建一个基础的层叠上下文。为了避免它的影响,我这里让box再创建一个层叠上下文(有定位且有z-index)。
于是这六个子元素和自身的background/border其实都在这个box的层叠上下文中,就得遵守同一层叠上下文的七层层叠等级规则。
六,层叠上下文层级和dom结构关系
首先需要明确的一点是层叠上下文层级和dom结构没有明确的映射关系。但层叠上下文层级又基于dom结构生成。
主要是因为层叠上下文支持嵌套,在一个层叠上下文中还可以创造新的层叠上下文。但当未创建新的层叠上下文时,不管dom结构是不是父子关系,都是在同一层叠上下文之间,用七层层叠等级规则执行判断。
这里将上文的代码修改为:
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
height: 100vh;
background: pink;
}
/* 新创建一个层叠上下文 */
.box{
height: 350px;
background: greenyellow;
position: relative;
z-index:1;
}
.total{
height: 100px;
width: 300px;
text-align: center;
position: absolute;
}
.first-item{
background: yellow;
top: 10px;
left:10px;
}
.second-item{
background: blueviolet;
display: block;
top: 50px;
left:40px;
z-index: 1;
}
.a-1{
background: red;
height: 20px;
position:absolute;
left: 200px;
top:30px;
z-index: 5;
}
.a-2{
background: rgb(229, 20, 236);
height: 20px;
position: absolute;
z-index: -1;
right: -20px;
top: 27px;
}
</style>
<div class="box">
<div class="first-item total">
<div class="a-1">a-1</div>
<div class="a-2">a-2</div>
</div>
<div class="second-item total"></div>
</div>
</body>
</html>
实现的效果:
就像这里,box创建了一个新的层叠上下文,又因为它具备子元素first-item和second-item,所以这两个元素必然是在同一个层叠上下文(box创建的)中。
而second-item不再有子元素,不用继续考虑,接下来看first-item。
因为first-item并没有创建新的层叠上下文(虽然有定位,但z-index未设置),则它的子元素也和它一样在当前box创建的层叠上下文中。
也就是说,first-item和second-item,a-1以及a-2这四者是在同一个层级上下文中进行堆叠,即使a-1和a-2是first-item的子元素。
于是根据七层层叠规则得到:
box:创建该层叠上下文,它的background在最下面(草绿色)
first-item:block块级盒子(黄色)
second-item:z-index=1(紫色)
a-1:z-index=5(红色)
a-2:z-index=-1(粉色)
这个和上图中结果一致。这就是为啥说:**dom结构和层叠上下文没有严格的映射关系。**并不是一层dom就生成一个层叠上下文。
另外,**层叠上下文层级又基于dom结构生成。**意思就是低层级的层叠上下文,无论内部元素的z-index多大,是无论如何无法盖在高层级别元素上方的。
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
height: 100vh;
background: pink;
}
/* 新创建一个层叠上下文 */
.box{
height: 350px;
background: greenyellow;
position: relative;
z-index:1;
}
.total{
height: 100px;
width: 300px;
text-align: center;
position: absolute;
}
.first-item{
background: yellow;
top: 10px;
left:10px;
z-index: 1;
}
.second-item{
background: blueviolet;
display: block;
top: 50px;
left:40px;
z-index: 2;
}
.a-1{
background: red;
height: 20px;
position:absolute;
left: 200px;
top:30px;
z-index: 5;
}
.a-2{
background: rgb(229, 20, 236);
height: 20px;
position: absolute;
z-index: -100;
right: -20px;
top: 27px;
}
</style>
<div class="box">
<div class="first-item total">
<div class="a-1 total">a-1</div>
<div class="a-2 total">a-2</div>
</div>
<div class="second-item total"></div>
</div>
</body>
</html>
对应实现的效果和层叠上下文层级关系如下图,first-item的层级比second-item低,比box高,因此,first-item中的元素无论z-index设置多高,都无法超过second-item的内容。无论z-index设置多低,都无法低于box(草绿色),甚至无法低于创建该层叠上下文的背景色(该层叠上下文中层叠等级最低,下图黄色)。
七,判断元素层叠顺序的套路
理解了层叠上下文,层叠等级,层叠上下文层级三者的关系,就可以用这个来判断两元素的遮盖情况:
1,看两个元素所在的层叠上下文层级。高层元素必然遮盖低层元素。
2,若在同一层叠上下文层级,也就是在同一个层叠上下文中,就需要看七层层叠等级。谁高谁在上面
3,如果都一样,后写的盖住之前写的。
八,案例练习
8.1,多层dom结构,但实际是同一层级上下文的情况
<!DOCTYPE html>
<html lang="en">
<body>
<style>
body{
height: 100vh;
background: pink;
}
/* 新创建一个层叠上下文 */
.box{
height: 350px;
background: greenyellow;
position: relative;
z-index:1;
}
.total{
height: 100px;
width: 300px;
position: absolute;
}
.first-item{
background: yellow;
top: 10px;
left:10px;
}
.a-1{
width: 300px;
height: 100px;
background: green;
float: left;
top: 10px;
left: 10px;
position: absolute;
}
.b-2{
width: 300px;
height: 100px;
background: blue;
position: absolute;
top: 40px;
left: 20px;
z-index: 100;
}
.second-item{
background: orange;
display: block;
top: 30px;
left:50px;
z-index: 2;
}
</style>
<div class="box">
<div class="first-item total">
<div class="a-1">float绿色</div>
<div class="a-2">
<div class="b-2">100蓝色</div>
</div>
</div>
<div class="second-item total">2橙色</div>
</div>
</body>
</html>
生成的效果如下图,因为first-item 并没有生成新的层叠上下文,所以first-item ,second-item,a-1,a-2,b-2这几个元素都是在同一个层叠上下文中,直接利用七个层叠等级规则判断就好:
8.2,多层dom结构,多个层叠上下文层级的情况
给上文的first-item增加一个样式:
.first-item{
z-index: 1;
}
效果变成:
这是因为first-item也创建一个层叠上下文,和second-item创建的并列,并且上下文层级低于second-item。于是即使b-2的z-index:100也无法高于second-item。
九,总结
对于z-index的使用规律,MDN实际上就简单一句话:”z-index越大则越上层,有爹则拼爹“。
实际上,则是:
1,同一个层叠上下文中,z-index越大则越上层。
2,不同层级上下文,则层级越高越上层。
3,层级等级和层叠等级相同的,则后来的覆盖之前的。
其实很多人误解的一点就是以为dom结构和层叠上下文层级并没有严格的映射关系。a元素的n层孙元素,有可能和a是在同一个层叠上下文中的。