掌握CSS中的z-index
z-index
是一个用于控制文档中各层排序的属性。z-index
值较高的元素出现在值较低的元素之上。就像页面上的x轴和y轴决定一个元素在水平和垂直方向上的位置一样,z-index
,控制它们在z轴上相互堆叠的方式。
默认的堆叠
当我们编写HTML时,出现在文档中较低位置的元素,自然会堆叠在较高位置的元素之上:
<body>
<header class="site-header"></header>
<main class="site-content"></main>
<footer class="site-footer"></footer>
</body>
在这个HTML片段中,footer
将堆叠在主内容区之上,而主内容区将堆叠在header
之上,如果它们都被定位为相互重叠。
元素可以通过使用position
属性和偏移属性top
,right
,bottom
和left
的组合进行重叠。
如果我在每一个元素上设置position: absolute
,它们都会在彼此的上面布局。footer
在文档中排在最后,所以默认情况下会堆叠在前两个元素的上面:
.site-header, .site-content, .site-footer {
position: absolute;
width: 400px;
padding: 20px;
}
.site-header {top: 0; left: 0;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px;}
如果我使用偏移属性,top
和left
,我们可以更清楚地看到顺序。
堆叠的背景
虽然使用position: absolute
,创建了相互重叠的元素,但我们还没有创建所谓的堆叠上下文。
堆叠上下文可以通过以下任何一种方式创建:
- 一个位置为
absolute
或relative
的元素和一个不属于z-index
的值auto
- 一个具有
z-index
值的柔性框项目,但不是auto
- 一个元素,其
opacity
小于1 - 一个元素,其
transform
被设置为任何其他值。none
到目前为止,创建和使用堆叠上下文的最常见方式是这个列表中的第一个例子,所以让我们先关注一下这个例子。
回到前面的例子,我们有三个元素被放置在彼此的顶部,但目前,他们没有一个z-index
。
z-index
属性允许我们控制堆叠的顺序。
如果我把z-index: 1
设置在footer
上,z-index: 2
设置在main
上,z-index: 3
设置在header
上,那么默认的堆叠顺序就可以完全颠倒。
这在表面上看起来很简单;z-index
越高,元素的堆叠就越高--所以一个z-index: 9999
总是在z-index: 9
的上面。 不幸的是,它比这要复杂一些。
z-index
在堆叠语境中
<header class="site-header blue">header</header>
<main class="site-content green">content
<div class="box yellow"></div>
</main>
<footer class="site-footer pink">footer</footer>
如果我在site-content
容器内添加一个盒子,并将其定位在右下角之外,我们可以看到它在绿色盒子之上,粉色盒子之下:
.box {
position: absolute;
bottom: -25px;
right: -25px;
z-index: 4; /* won't work :( */
width: 75px;
height: 75px;
border: 1px solid #000;
}
.site-header {top: 0; left: 0; z-index: -1;}
.site-content {top: 50px; left: 50px;}
.site-footer {top: 100px; left: 100px; z-index: 3;}
根据我们对z-index
的了解,我们可能会认为,要使这个黄色的盒子出现在粉色的盒子上面,我们可以为z-index
设置一个更高的值。
如果我设置z-index: 4
,它比z-index: 3
高,我们就会看到没有变化。人们通常试图通过尝试一个非常大的数字(如9999
)来强制堆叠,但这样做也没有效果。在代码库中看到这样的z-index
,有点像代码的味道,所以如果可以的话,尽量避免。
我们无法得到黄色方框在粉色方框上面的理想结果的原因是由于z-index
在堆叠环境中的行为方式。
为了证明这一点,让我们看一个稍微复杂的例子,我从MDN网站上借用了这个例子:
<header class="site-header blue">
<h1>Header</h1>
<code>position: relative;<br/>
z-index: 5;</code>
</header>
<main class="site-content pink">
<div class="box1 yellow">
<h1>Content box 1</h1>
<code>position: relative;<br/>
z-index: 6;</code>
</div>
<h1>Main content</h1>
<code>position: absolute;<br/>
z-index: 4;</code>
<div class="box2 yellow">
<h1>Content box 2</h1>
<code>position: relative;<br/>
z-index: 1;</code>
</div>
<div class="box3 yellow">
<h1>Content box 3</h1>
<code>position: absolute;<br/>
z-index: 3;</code>
</div>
</main>
<footer class="site-footer green">
<h1>Footer</h1>
<code>position: relative;<br/>
z-index: 2;</code>
</footer>
.blue {background: hsla(190,81%,67%,0.8); color: #1c1c1c;}
.purple {background: hsla(261,100%,75%,0.8);}
.green {background: hsla(84,76%,53%,0.8); color: #1c1c1c;}
.yellow {background: hsla(61,59%,66%,0.8); color: #1c1c1c;}
.pink {background: hsla(329,58%,52%,0.8);}
header, footer, main, div {
position: relative;
border: 1px dashed #000;
}
h1 {
font: inherit;
font-weight: bold;
}
.site-header, .site-footer {
padding: 10px;
}
.site-header {
z-index: 5;
top: -30px;
margin-bottom: 210px;
}
.site-footer {
z-index: 2;
}
.site-content {
z-index: 4;
opacity: 1;
position: absolute;
top: 40px;
left: 180px;
width: 330px;
padding: 40px 20px 20px;
}
.box1 {
z-index: 6;
margin-bottom: 15px;
padding: 25px 10px 5px;
}
.box2 {
z-index: 1;
width: 400px;
margin-top: 15px;
padding: 5px 10px;
}
.box3 {
z-index: 3;
position: absolute;
top: 20px;
left: 180px;
width: 150px;
height: 250px;
padding-top: 125px;
text-align: center;
}
这里我们有一个header
、footer
、main
、content
的容器,但在site-content
里面,我们有三个盒子,它们都被定位了,并被赋予一个z-index
。
让我们先看一下这三个主要的容器--header
,footer
和main
。
header
的z-index
是 5,所以出现在main
content
的上面,而z index: 4
是 5。footer
的z-index
是 2,所以出现在main
的下面,而z-index
更高,是 4。到目前为止,一切都好吗?很好。
在main
容器内的三个盒子里,事情变得有点令人困惑。
内容框1的z-index
为6,但似乎是在header
的下面,较低的z-index
为5。
内容框2的z-index
为1,但出现在footer
的上方,而z-index
的数值较高。
那么,发生了什么事?
所有这些都可以通过以下事实来解释:所有的z-index
值都是在其父级堆叠环境中解决的。因为父容器.site-content
的z-index
比footer
高,所以.site-content
中的任何定位元素都在该上下文中被评估。
思考堆叠上下文中的堆叠顺序的一个好方法是,把它看作是嵌套有序列表中的一个子项。
这可以写成如下:
- 页首:
z-index: 5
- 主要的:
z-index: 4
- 盒子1:
z-index: 4.6
- 盒子2:
z-index: 4.1
- 方框3:
z-index: 4.3
- 盒子1:
- 页脚:
z-index: 2
因此,尽管header
是z-index: 5
,content
框1是z-index: 6
,它的渲染顺序是4.6,仍然小于5。因此,content
框1出现在header
的下面。
一开始有点混乱,但经过实践,它确实开始有意义了!
z-index
只对定位的元素有效
如果你想控制元素的堆叠顺序,你可以用z-index
。但是z-index
,只有当该元素的position
值为absolute
、relative
或fixed
,才会生效。
用位置精确地放置元素,对于建立复杂的布局或有趣的UI模式是很好的,但通常想要控制堆叠顺序而不把元素从它在页面上的原始位置移开。
如果是这种情况,你可以只设置position: relative
,而不为top
,right
,bottom
或left
提供任何值。元素将保持在页面上的原始位置,文档流不会被打断,z-index
值将生效。
你可以有负的z-index
分层元素通常是为了建立复杂的形状或UI组件。这通常意味着将元素叠加在一起,其数值不断增加z-index
。要将一个元素放在另一个元素的下面,它只需要有一个较低的数值z-index
,但这个较低的数值可以是负数。
这一点很有用的一个领域是在使用伪元素并希望将其置于其父元素的内容之后。
由于堆叠上下文的工作方式,如果任何:before
或:after
元素要被定位在其父元素的文本内容后面,就需要一个负值z-index
。
看看下面的Codepen,并试验一下z-index
的各种值。
z-index
策略
让我们用我在整个项目中应用z-index
的一个简单策略来总结一下。
前面我们对z-index
的值使用了个位数的增量,但如果你想在两个被设置为z-index: 3
和z-index: 4
的元素之间添加一个新元素呢?你必须改变很多数值--可能是在整个代码库中,这可能会成为问题,并容易在网站的其他部分出现CSS损坏。
使用100的步骤来设置z-index
在处理z-index
,像这样的代码并不少见:
.modal {
z-index: 99999;
}
在我看来,这只是黑客行为(而且在附加上!important
,情况会更糟)。看到这样的值往往是开发者不理解堆叠上下文的症状,并试图强迫一个层在另一个层的上面。
与其使用像9999
或53
或12
这样的任意数字,我们可以系统化我们的z-index
比例,为程序带来更多的秩序。这并不是(仅仅)因为我有开发者强迫症。说实话。
我的z-index
,而不是使用个位数的增量,我使用100的增量:
.layer-one {z-index: 100;}
.layer-two {z-index: 200;}
.layer-three {z-index: 300;}
我这样做是为了保持事情的条理性,同时也是为了注意在整个项目中使用的众多不同的层。另一个好处是,如果需要在两个图层之间添加一个新的图层,有99个潜在的值可以在这之间选择。
当建立一个z-index
系统时,这种手动方法是相当可靠的,但当与像Sass这样的预处理器的力量相结合时,可以变得更加灵活。