前言
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
将会层叠在main
内容区域之上,main
将会层叠在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
可以创建相互重叠的元素,但我们还没有创建所谓的层叠上下文。
层叠上下文可以通过以下任意方式进行创建:
- 元素的
position
属性值为absolute
或者relative
,且z-index
值不为auto
。 flex
容器的子元素,且z-index
值不为auto
。opacity
属性值小于1
的元素。transform
属性值不为none
的元素。
到目前为止,最常见的创建和使用层叠上下文的方式是上述列表中的第一种,所以让我们多花点时间来关注它。
回到先前的示例,我们有三个元素彼此重叠,但目前为止它们并没有z-index
值。
z-index
属性允许我们控制层叠的顺序。如果在footer
元素上设置z-index: 1
,在main
元素上设置z-index: 2
,以及在header
元素上设置z-index: 3
,那么默认层叠顺序将会完全颠倒。
表面上看起来很简单,更高的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
,并将其定位在右下角之外,我们可以看到它位于绿色盒子的上面和粉色盒子的下面。
.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
高,但是并没有看到任何变化。人们通常视图通过设置一个巨大的数字来强制改变层叠顺序,比如说设置z-index: 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
容器,就跟以前一样。但是在site-content
内部,我们有三个盒子,它们都被定位了,并赋予了z-index
。
让我们首先看一下三个主要容器 - header
,footer
和main
。
header
的z-index
值为5,因此出现在z-index
值为4的main
之上,footer
的z-index
值为2,因此出现在main
之下。目前为止一切顺利吧?很好。
main
容器内的三个盒子让事情变得扑朔迷离起来。
Content box 1的z-index
值为6,但出现在header
下面,而header
的z-index
值为5。
Content box 2的z-index
值为1,但出现在footer
上面,而footer
的z-index
值为2。
这究竟发生了啥?
所有疑虑都可以通过以下事实来解释:所有的z-index
值都是在其父级层叠上下文中生效的。因为父容器.site-content
相比footer
而言,有一个更高的z-index
值,因此.site-content
中的任何定位元素都将在该上下文中计算。
在层叠上下文中思考层叠顺序的一个好方法是,把它看作是嵌套有序列表中的一个子项目。按照这种思路可以写成如下格式:
-
Header:
z-index: 5
-
Main:
z-index: 4
- Box 1:
z-index: 4.6
- Box 2:
z-index: 4.1
- Box 3:
z-index: 4.3
- Box 1:
-
Footer:
z-index: 2
因此,即使header
是z-index: 5
,content box 1是z-index: 6
,但content box 1的渲染顺序是4.6,仍然小于5。因此,content box 1在header
下面。
刚开始确实有点乱,但随着练习,开始有眉目了。
z-index只作用于定位元素
如果你想控制元素的层叠顺序,你可以使用z-index
达到目的。但是,只有当该元素的position
值为absolute
、relative
或fixed
时,z-index
才会产生影响。
用position
精确地放置元素,对于建立复杂的布局或有趣的UI模式来说是不错的。但通常只是想要控制层叠的顺序,而不把元素从它在页面上的原始位置移开。
如果是这种情况,你可以只设置position: relative
,而不提供top
、right
、bottom
或left
的任何值。该元素将保持在其在页面上的原始位置,文档流不会被打断,z-index
值将会生效。
z-index可以是负值
分层元素通常是为了建立复杂的图形或UI组件。这通常意味着将分层元素彼此重叠,并设置不断增加的z-index
值。要把一个元素放在另一个元素的下面,它只需要有一个较低的z-index
值,但这个较低的值可以是负值。
当使用伪元素并希望将其定位在其父元素的内容之后时,负值的z-index
是非常有用的。
由于层叠上下文的工作方式,对于任何:before
或:after
元素,如果它们要被定位在其父元素的文本内容后面,那么它们需要一个负的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
比例系统化,为程序带来更多秩序。这里我将使用以100为基础进行递增的z-index
值。
.layer-one {z-index: 100;} .layer-two {z-index: 200;} .layer-three {z-index: 300;}
我这样做是为了保持事情的条理性,同时也是为了注意整个项目中使用的众多不同的层。另一个好处是,如果需要在其他两个图层之间添加一个新的图层,有99个潜在的值可以挑选。
当建立一个z-index
系统时,这种手动方法是相当可靠的,但当与像Sass这样的预处理器的能力相结合时,可以变得更加灵活。