<br /> —— 封面 层叠上下文关系图
层叠上下文在css中是一个重要的基础概念,之所以出现层叠是因为在页面中元素的排列是一个三维的空间,而我们只能看到一个平面,因此产生了层叠。
下面这篇文章将会从一个普通盒子的层叠逐步讲到层叠上下文,以及丰富的例子来直观地看到层叠的效果。
问题引入——盒子的border与background
background的范围是从哪里到哪里的?
content-box的情况下<style>div {/* box-sizing: border-box; */border: 10px solid rgba(255, 0, 0, 1);width: 100px;height: 100px;background-color: blue;}</style>
<body><div></div></body>
我们在Chrome的调试工具中调试这个透明度(选中数值,按
option + 方向键↓可以让数值一点一点下降)

border-box的情况<style>div {box-sizing: border-box;border: 10px solid rgba(255, 0, 0, 1);width: 100px;height: 100px;background-color: blue;}</style>

由此得出背景的范围是从**border**外部开始的,**background**包括了**border**
从三维的角度看,border覆盖在background上面
层叠上下文
注:这里只探讨了定位引起的层叠
一个普通div的层叠关系
由上面的例子可以知道,在一个普通的盒子里面他的border和background是有摆放顺序的。
但是对于我们用户来说,我们只能看到一个平面。
我们可以通过测试得出一个普通的盒子的层级
- 根据上面的例子,我们已经知道了,
border是在background上面的。
块级子盒子和border谁在上谁在下?
<style>.outter{width: 100px;height: 100px;border: 10px solid red;background-color: green;}.inner {width: 20px;height: 20px;/* 让块级子盒子移动到border上 */margin-left: -5px;background-color: purple;}</style>
<body><div class="outter"><!-- 块级子盒子 --><div class="inner"></div></div></body>
结果如图所示,块级子盒子能够压住border,而background肯定也是覆盖在上面了。

接下来我们再来看看内联子盒子(文字)与块级子盒子的关系
内联子盒子(文字)与块级子盒子的关系
<style>.outter{width: 100px;height: 100px;border: 10px solid red;background-color: green;}.inner {width: 20px;height: 20px;margin-top: -10px;background-color: purple;}</style>
<body><div class="outter"><!-- 内联子盒子 -->我是一句话,我的级别很高<!-- 块级子盒子 --><div class="inner"></div></div></body>
效果如图所示,内联子元素肯定是比块级子元素的层级高的

最后我们在对比一下,浮动的块级子元素,普通的块级子元素和内联子元素
浮动的块级子元素,普通的块级子元素和内联子元素
<style>.outter{width: 100px;height: 100px;border: 10px solid red;background-color: green;}.inner {width: 20px;height: 20px;margin-top: -10px;background-color: purple;border: 1px solid chartreuse;}.inner_float {float: left;width: 30px;height: 30px;margin-top: -30px;margin-left: 20px;background-color:fuchsia;}</style>
<body><div class="outter"><!-- 内联子盒子 -->我是一句话,我的级别很高<!-- 块级子盒子 --><div class="inner"></div><!-- 浮动的块级子盒子 --><div class="inner_float"></div></div></body>
效果图如下,我给普通的盒子加上了绿色的边框,这样我们可以发现float的子盒子会盖住普通的子盒子,但是无法盖住内联子盒子。

现在我们就可以对普通的盒子进行一个总结了
对于一个普通的盒子来说,层级是这样的
内联元素(文本)= inline-block > 浮动的块级子元素 > 普通的块级子元素 > border > background
立体的图像如下图所示:

为什么内联子元素的层级会比块级和浮动高呢?
张鑫旭老师这有个很好的说法。
诸如
border/background一般为装饰属性,而浮动和块状元素一般用作布局,而内联元素都是内容。网页中最重要的是什么?当然是内容了哈,对不对! 因此,一定要让内容的层叠顺序相当高,当发生层叠是很好,重要的文字啊图片内容可以优先暴露在屏幕上。
同一层叠上下文的层叠准则
- 如果设置了
z-index那么谁的z-index值大,谁就在上面。 - 当层叠顺序相同的时候,dom流后面的元素会压住前面的元素。
我们刚刚讨论的是一个普通的元素的层叠水平,我们并没有创建一个层叠上下文。
创建层叠上下文
如何创建层叠上下文呢,其实我们一开始就已经创建了一个层叠上下问,和块级格式上下文一样, html 标签会创建一个层叠上下文。
这就是为什么,绝对定位元素在left/top等值定位的时候,如果没有其他定位元素限制,会相对浏览器窗口定位的原因。
还有一种创建层叠上下文的方法就是:z-index值为数值的定位元素的传统层叠上下文。
当 position 的值不是 static ,且 z-index 设置了值得时候,就会创建层叠上下文。
当我们不设置
z-index的时候,z-index的值默认是auto。
现在这个时候我们再看看完整的层叠上下文关系图
图片来自于:w3cplus
当然了,根据我们之前在上文的探究的实际上background会在border下面。
重新梳理与相关例子
开天辟地:
当 html 标签出来的时候,就创建了一个层叠上下文,这就是我们一开始的世界。
一片混沌:
这个时候,我们还没有所谓的定位,大家都是普通的元素,但是也有长幼之分。
也就是 background < border < 普通块级元素 < 普通浮动元素 < 内联元素/文本 。
这个就是上面的图,代码可以看上面的。
罪恶的阶级:
在大家一片混沌的年代(也就是只根元素创建的层叠上下文的年代),大家是没有什么太大差别的(都是一家人,只有长幼之分)。
但是——大人,时代变了。
这个时候出现了一个叫定位的东西,和一个叫 z-index 的东西。这两个东西打破了原来的平衡,出现了新的阶级(官府)。
我们作为上帝,可以给某一个元素设置定位,以及 z-index 。我们把他的等级( z-index )设置成1。这个时候,我们就创建一个新的阶级(新的层叠上下文)。
<style>.containner {height: 300px;width: 300px;padding: 30px;background-color: yellow;}.nomal {background-color: pink;width: 60px;height: 60px;}.test {position: relative;z-index: 1;top: -30px;background-color: red;}</style></head><body><div class="containner"><div class="nomal">我是一个平民</div><div class="test">我是天龙人</div></div></body>

我们可以从图中明显的看到,我们的天龙人,作为新的阶级(新的层叠上下文),比原来上下文的内联元素等级都高。
因此也我们的准则可以再加上一条了也就是:
background < border < 普通块级元素 < 普通浮动元素 < 内联元素/文本 < 拥有定位的元素
但是我们并没有好好的讨论 z-index 。这个 z-index 可以理解为地位,当我们的都在同一级的上下文(这里是由 **html** 创建的上下文)创建新的层叠上下文的时候, z-index 的值越大,相对应的层级就越高。
<style>.containner {height: 300px;width: 300px;padding: 30px;background-color: yellow;}.upper1 {position: relative;z-index: 1;background-color: pink;width: 100px;height: 60px;}.upper100 {position: relative;z-index: 100;top: -25px;background-color: red;}</style><body><div class="containner"><div class="upper1">我是天龙人, 等级为1</div><div class="upper100">我是天龙人, 等级为100</div></div></body>

这个时候我们可以看到,即使大家都是天龙人,也有等级之分( z-index )。 z-index 越大,层级越高。
狐假虎威:
在上面我们看到了,将一个元素设置定位,然后再给他一个 z-index 就会创建出一个新的层叠上下文。但是我们并没有去讨论默认的情况。也就是 z-index = auto
如果我们给一个元素设置定位,但是并没有给它设置 z-index 的话,那么 z-index 的默认值是 auto 。
为什么要把这一小节的标题叫做狐假虎威呢?因为如果你没有给 z-index 设置数值的话,他是不会创建新的层叠上下文的。但是它在页面中的显示效果却和 z-index = 0 是一样的。
<style>.containner {height: 300px;width: 300px;padding: 30px;background-color: yellow;}.normal {position: relative;z-index: 1;background-color: pink;width: 100px;height: 60px;}.upper {position: relative;z-index: 100;top: -30px;background-color: red;}</style><body><div class="containner"><div class="normal">我是平民</div><div class="upper">我是假的天龙人</div></div></body>

用来表示
z-index=auto和z-index=0有区别的例子,请看下面
打狗看主人:
什么叫打狗也要看主人呢,比如我们某一个层级中,分别创建两个层叠上下文, z-index 分别设置为1和2。这个时候我们再在这两个层叠上下文中分别创建一个层叠上下文, z-index 分别是10和1。
画图理解一下就是:

这个时候,如果我们将 层叠上下文b1 和 层叠上下文a1 叠在一起的时候,显示的结果是 b1 压在了 a1 上。
为什么呢?明明 a1 的 z-index = 10 明显比 b1 的 z-index 要大啊。这就是因为,打狗要看主人,对于层叠上下文 a 与 b 来说, b 的等级是要比 a 的,因此无论 a 里面的层级有多高,都只能在 a 这个上下文里面有效果。
我们依然可以用代码验证一下:
<style>.containner {height: 300px;width: 300px;padding: 30px;background-color: yellow;}.a {position: relative;z-index: 1;width: 100px;height: 100px;}.a1 {position: relative;z-index: 10;background-color: pink;}.b {position: relative;width: 100px;height: 100px;z-index: 2;top: -63px;}.b1 {position: relative;z-index: 1;background-color: red;}</style><body><div class="containner"><div class="a"><div class="a1">我是a1, 我在a里面地位高</div></div><div class="b"><div class="b1">我是b1, 我在所有地方地位都高</div></div></div></body>

这个时候我们再来看一个例子:就是关于 z-index = auto 和 z-index = 0 的区别的。
还是上面那个例子作为原型,我们只需要修改一个地方,结果就会完全不一样。是的,我们将 层叠上下文a 的 z-index 改成 auto 。这样就会导致结果完全反过来—— a1 反而压住 b1 。
但是这里我们为了比较值为 auto 和 0 的区别,我们将a的层级改为auto,将b的层级改为0。

代码就是修改两个地方的 z-index ,不给出了。直接看结果
出现这种情况的原因就是 z-index = auto 的时候不会创建层叠上下文。也就是说此时 a 并不是层叠上下文, a1 才是在 html 创建的这个层叠中所创建的层级上下文,而另一个在 html 这个层级中创建的层级上下文是 b ,而 b 的 z-index 是0。毫无疑问 10>0 ,所以 a1 肯定会覆盖 b ,也就肯定会覆盖到 b1 了。
虎落平阳
本来,给一个元素设置定位,并且给定一个数值给 z-index ,这个元素就一飞冲天,凌驾于所有普通元素之上,但是天有不测。如果你给一个元素的 z-index 设置为负值的话,那么他虽然也会创建层级上下文,但是他并不会凌驾于普通元素之上,反而是只比 border 和 background 高。
<style>/* .containner {height: 300px;width: 300px;padding: 30px;background-color: yellow;} */.normal {width: 50px;height: 50px;background-color: pink;}.loser {position: relative;z-index: -1;top: -10px;width: 100px;height: 100px;background-color: red;}</style><body><div class="containner"><div class="normal">我是平民</div><div class="loser">我现在虽然是层级上下文,但我却是个fw</div></div></body>

你看,此时连平民(普通的元素)都能遮住层叠上下文了。
再来一个例子
我们可以看到,我刚刚的代码,我是把 containner 注释掉了,我们现在看看不注释掉的效果
你会发现, 我们所创建出来的层叠上下文居然不见了,其实它只是被黄色遮住了。
出现这样的原因其实很简单,因为 z-index 为负值的层叠上下文所处的位置是位于创建他的层叠上下文(这个例子中指的是 html )的背景和border上的。而我们的黄色背景并不是加在 html 上的,而是一个普通的 div 上,因此他才不会显示出来。
但是很奇怪的一点是,如果你给 body 加上背景色的话,即使 body 并没有创建层叠上下文, z-index 为负值的层叠上下文,并不会被body的背景所遮盖。这与我们刚刚的分析相悖。我并不清楚为什么,可能所学的知识还不够。
我又进行了一次探究,当我给 body 设置 border ,然后再把 containner 强行移动到边框上的时候,符合我们分析的结果(border把 z-index 值为负的层叠上下文遮住)出现了。
代码:
<style>body {border: 100px solid yellow;}.containner {/* 我这么做是为了将使body的边框能覆盖normal和loser */margin-left: -50px;}.normal {width: 50px;height: 50px;background-color: pink;}.loser {position: relative;z-index: -1;top: -10px;width: 100px;height: 100px;background-color: red;}</style><body><div class="containner"><div class="normal">我是平民</div><div class="loser">我现在虽然是层级上下文,但我却是个fw</div></div></body>

因此,我猜测,可能浏览器动了手脚。毕竟给 body 设置背景色的时候,浏览器也搞了些小动作。
总结
都在这张图里了。
图片来自于:w3cplus
