关于z-index的真正问题是,很少有人理解它到底是怎么用。其实它并不复杂,但是如果你从来没有花一定时间去看具体的z-index相关文档,那么你很可能会忽略一些重要的信息。
不相信我吗?好吧,看看你能否解决下面这个问题:
问题:
在 接下来的HTML里 有三个<div>元素,并且每个<div>里包含一个<span>元素。每 个<span>被分别给定一个背景颜色:红、绿、蓝。每个<span>被放置到文档的左上角附近,部分重叠着其他 的<span>元素,这样你就可以看到哪些是被堆叠在前面。第一个<span>有一个z-index的值为1,而其他两个没有任 何z-index值。
以下就是这个HTML和它的基本CSS。
HTML代码
1 2 3 4 5 6 7 8 9 |
<div> <span>Red</span> </div> <div> <span>Green</span> </div> <div> <span>Blue</span> </div> |
CSS代码
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.red, .green, .blue { position: absolute; } .red { background: red; z-index: 1; } .green { background: green; } .blue { background: blue; } |
挑战:
尝试使红色<span>元素堆在蓝色和绿色<span>的后面,不要打破以下规则:
- 不要以任何方式改变HTML标记
- 不要添加/修改任何元素的z-index属性
- 不要添加/修改任何元素的position属性
如果你找到了答案,那么它应该像下面这样:
1 2 3 4 5 6 7 8 9 |
<div> <span>Red</span> </div> <div> <span>Green</span> </div> <div> <span>Blue</span> </div> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
div:first-child { opacity: .99; } .red, .green, .blue { position: absolute; } .red { background: red; z-index: 1; } .green { background: green; } .blue { background: blue; } |
解决方案:
这个解决方法是在第一个<div>里(红色<span>的父节点)添加一个小于1的opacity属性值。下面就是被添加的CSS的例子:
1 2 3 |
div:first-child { opacity: .99; } |
如果你现在很震惊,但是仍然百思不得其解,并且不相信opacity能决定哪个元素堆在前面,欢迎来社区提问,当第一次在这个问题上被困扰时我同样很震惊。
希望接下来的内容能够让你对这个问题更清楚些。
堆栈顺序
Z-index看上去如此简单:高的z-index堆在低的z-index的前面,对吗?这实际上是错的,是z-index问题的一部分。它看上去如此的简单,以至于很多开发者没有花相应的时间去读相关的规则。
每一个在HTML文档中的元素既可以在其他元素的前面,也可以在其他元素的后面。这就是所谓的堆栈顺序。决定这个顺序的规则被十分清楚的定义在说明文档中,但是就像之前我已经提到过,这些文档没有被大多数开发者们完全弄明白。
当z-index和position属性不被包括在内时,这些规则相当简单:基本上,堆栈顺序和元素在HTML中出现的顺序一样。(好吧,其实是有一点复杂的,但是只要你不使用压缩边界来重叠行内元素,你可能不会遇到边界问题。)
当你把位置属性也包括在内介绍时,任何定位元素(和他们的子元素)都在非定位元素前被显示出来。(说一个元素被“定位”意思是它有一个不同于静态的位置值,例如相对的,绝对的,等等。)
最 后,当z-index被提及时,事情变的有点儿复杂。最初,很自然的假设带有高z-index值的元素会在带有低z-index值的元素前面,但是后来发 现没那么简单。首先,z-index只对定位元素起作用。如果你尝试对非定位元素设定一个z-index值,那么肯定不起作用。其次,z-index值能 创建堆栈上下文环境,并且突然发现看似简单的东西变的更加复杂了。
堆栈上下文
一组具有共同双亲的元素,按照堆栈顺序一起向前或向后移动构成了所谓的堆栈上下文。充分理解堆栈上下文是真正掌握z-index和堆栈顺序工作原理的关键。
每 一个堆栈上下文都有一个HTML元素作为它的根元素。当一个新的堆栈上下文在一个元素上形成,那么这个堆栈上下文会限制所有的子元素以堆栈的顺序存储在一 个特别的地方。那意味着一旦一个元素被包含在处于底部堆栈顺序的堆栈上下文中,那么就没有办法先出现于其他处于更高的堆栈顺序的不同堆栈上下文元素,就算 z-index值是十亿也不行!
现在,堆栈上下文有三种方法可以在一个元素上形成:
- 当一个元素是文档的根元素时(<html>元素)
- 当一个元素有一个position值而不是static,有一个z-index值而不是auto
- 当一个元素有一个opacity值小于1
前两种形成堆栈上下文的方法具有很大意义并且被广大Web开发者所理解(即使他们不知道这些被叫做什么)。第三种方法(opacity)几乎从来没在w3c说明文档之外被提及过。
用堆栈顺序决定一个元素的位置
实际上,为一个页面上的所有元素决定全局堆栈顺序(包括边界、背景、文本节点、等等)是极度复杂的,并且远远超越了本文讲述的范围(再一次,参考文档)。但是我们最大的目的,就是基本了解这个顺序,它能够在很长一段时间内帮助我们提高CSS开发的可预测性。所以,让我们打破顺序,分解为独立的堆栈上下文。
在同样的堆栈上下文里的堆栈顺序
下面是几条基本的规则,来决定在一个单独的堆栈上下文里的堆栈顺序(从后向前):
- 堆栈上下文的根元素
- 定位元素(和他们的子元素)带着负数的z-index值(高的值被堆叠在低值的前面;相同值的元素按照在HTML中出现的顺序堆叠)
- 非定位元素(按照在HTML中出现的顺序排序)
- 定位元素(和他们的子元素)带着auto的z-index值(按照在HTML中出现的顺序排序)
- 定位元素(和他们的子元素)带着正z-index值(高的值被堆叠在低值的前面;相同值的元素按照在HTML中出现的顺序堆叠)
注 解:定位元素带有负的z-index值被在一个堆栈上下文中先排序,这意味着他们出现在所有其他元素的后面。正因如此,它使一个元素出现在自己父元素之后 成为可能,这以前通常是不可能的事。当然,这局限于它的父元素与它在同一个堆栈上下文,并且不是那个堆栈上下文的根元素。一个伟大的例子如Nicolas Gallagher的CSS不用图像降低阴影。
全局堆栈顺序
坚定的理解了为什么/什么时候新的堆栈上下文形成,同时掌握了同一个堆栈上下文的堆栈顺序,现在让你来找出一个特定元素将出现在全局堆栈里的顺序不是那么糟糕了吧?
避免错误的关键是能够发现新的堆栈上下文什么时候形成。如果你对一个元素设置了z-index值为十亿但是它没有在堆栈顺序中向前移动,检查一下它的祖先树,看是否它的父节点形成了堆栈上下文。如果是那样的话,你的z-index值即使有十亿也不会给你带来好处。
包扎救治
回到之前的原始问题,我已经重建了这个HTML的结构,添加了一些注释,每一个标签指明了它在堆栈里的顺序。这个顺序是假设最初的CSS。
1 2 3 4 5 6 7 8 9 |
<div><!-- 1 --> <span><!-- 6 --></span> </div> <div><!-- 2 --> <span><!-- 4 --><span> </div> <div><!-- 3 --> <span><!-- 5 --></span> </div> |
当我们添加opacity到第一个<div>,堆栈顺序像下面这样改变:
1 2 3 4 5 6 7 8 9 |
<div><!-- 1 --> <span><!-- 1.1 --></span> </div> <div><!-- 2 --> <span><!-- 4 --><span> </div> <div><!-- 3 --> <span><!-- 5 --></span> </div> |
span.red曾经的顺序是6,但现在改为1.1。我已经使用“.”来标注一个新的上下文环境的形成。span.red现在是那个新的上下文的第一个元素。
现 在似乎更清晰了,关于为什么红色盒子跑到其他盒子的后面。原始的例子只包含两个堆栈上下文,根元素和形成span.red的那个。当我们添加 opacity到span.red的父节点上,形成了第三个堆栈上下文,结果显示在span.red上的z-index值只能应用在那个新的堆栈上下文 中。因为第一个<div>(应用opacity的那个)和它的兄弟元素没有position或者z-index值的集合,他们的堆栈顺序是由 他们在HTML里的源顺序决定的,也就是说第一个<div>,和它的堆栈上下文里的所有元素被第二个和第三个<div>元素分 离。