看似简单的表象实际上并没有想象的简单。最近不知道啥情况,同事之间流行了一句话“这还不简单啊”。甭管遇见什么问题都是先抛出这句话出来。除了增加自信之外还附带了震慑菜鸟的功能,不过我是不敢苟同这句话。闲话不多说,老规矩原创文章有妹子镇楼。养眼的同时还能提升血压增加血流量,让脑子转的更快。
先从简单的定义开始理解。z-index
属性的定义是设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。很简单的一句话,字面上理解就是z-index
来决定谁在上面谁在下面的属性。尤其是对于有一定Photoshop基础的人来说理解这点很是简单。
专业点来说,该属性设置一个定位元素沿 Z轴的位置,Z轴定义为垂直延伸到显示区的轴。如果为正数,则离用户更近,为负数则表示离用户更远。这条Z轴垂直于显示器确“看不见”。和PS中的图层的意思很是相近。每一个DOM元素都会有一个z-index
值。
属性必然会有可能的值,z-index
属性的值比较简单,只有三种取值可能。
- auto:这是默认值。堆叠顺序与父元素相等。
- number:整数值,设置元素的堆叠顺序。(可设置负数)
- inherit:父元素继承 z-index 属性的值。
相信看到这里,各位大神都会觉得。我是在复制一些网上的信息过来拼凑成文章而已。我只能嘿嘿嘿嘿笑下。装逼时间还未到……
有些程序猿,尤其是后端大神对于我们前端工程师总是嗤之以鼻,言语内外总是带着一点点的优越感。哥我从来不信邪,都说CSS简单,可我认为越是简单的东西越是难掌握。因为简单会让人不去深入钻研,或是潜意识去忽略一部分东西,而这些东西有可能就是看上去简单的知识最核心的内容。
当我们在百度的搜索框做了一点点操作之后,就会有一个下拉框。只有搜索有一定热度的词才会被放到备选下拉框中。当我输入z-
两个字符的时候就会跳出如下图的内容:
其实我想笑,我知道是什么的弱智原因造成的无效。记得曾经有一位我比较崇拜的编程开发大神曾经很认真的问过我这个问题。真不知道那些偏激后端大神们的优越感从什么地方来的。在这里我就代表前端那些纯玩CSS的人来告诉那些看不起前端的人,让他们知道在不同的知识领域有不同的知识深度,那些你看上去很“肤浅”很“幼稚”的东西会让你焦头烂额。
要想彻底的把z-index
搞的很清晰,我们必须先从几个概念开始了解。。接下来会有大段的英文引用和蹩脚的翻译,请勿喷饭!
层叠上下文(stacking context),在MDN中的描述是
引文原文
Stacking context is the three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user who is assumed to be facing the viewport or the webpage.HTML elements occupy this space in priority order based on element attributes.蹩脚的翻译:
层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的 z 轴上延伸,HTML 元素依据其自身属性按照优先级顺序占用层叠上下文的空间。
也就是上文开头在解释概念的时候说的垂直于显示器的看不见的Z轴。
层叠等级(stacking level)
层叠等级顺序决定了同一个层叠上下文中元素在z轴上的显示顺序。其实也有叫层叠水平的,不过我还是习惯叫它层叠等级。
层叠顺序(stacking order)
关于层叠顺序网络上可以找到一张很著名的图,不过图有点点错误。为了节省时间我还是引用吧。
为什么说图有点错误呢?按照 W3官方 的说法,准确的 7 层为:
- the background and borders of the element forming the stacking context.
- the child stacking contexts with negative stack levels (most negative first).
- the in-flow, non-inline-level, non-positioned descendants.
- the non-positioned floats.
- the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
- the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
- the child stacking contexts with positive stack levels (least positive first).
蹩脚的翻译
- 形成层叠上下文环境的元素的背景与边框
- 拥有负 z-index 的子堆叠上下文元素 (负的越高越堆叠层级越低)
- 正常流式布局,非 inline-block,无 position 定位(static除外)的子元素
- 无 position 定位(static除外)的 float 浮动元素
- 正常流式布局,inline-block元素,无 position 定位(static除外)的子元素(包括 display:table 和display:inline )
- 拥有 z-index:0 的子堆叠上下文元素
- 拥有正 z-index: 的子堆叠上下文元素(正的越低越堆叠层级越低)
一个普通元素具有了层叠上下文,其层叠顺序就会变高。那它的层叠顺序究竟在哪个位置呢?这里需要分两种情况讨论:
如果层叠上下文元素不依赖z-index数值,则其层叠顺序是z-index:auto可看成z:index:0级别;
如果层叠上下文元素依赖z-index数值,则其层叠顺序由z-index值决定。
所以为什么定位元素会层叠在普通元素的上面?其根本原因就在于,元素一旦成为定位元素,其z-index就会自动生效,此时其z-index
就是默认的auto
,也就是0级别,根据上面的层叠顺序表,就会覆盖inline
或block
或float
元素。
而不支持z-index
的层叠上下文元素天然z-index:auto
级别,也就意味着,层叠上下文元素和未设置z-index
的定位元素是一个层叠顺序的,于是当他们发生层叠的时候,遵循的是“后来居上”准则。
层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。
如何去比较上下文的层级关系呢?在同一个层叠上下文中,则根据7阶层叠等级比较。若两元素在同一层叠水平,则后来居上,即在DOM流中处于后面的元素会覆盖前面的元素。
在不同的层叠上下文中,则直接比较父元素的层叠水平:
若父元素的z-index
不同,则z-index
数值越大,越在上面。
若父元素的z-index
相同,则在DOM流中处于后面的元素会覆盖前面的元素。
注:
比较时,先看两个元素是不是在同一个父元素之下,若不是,则一层层往上找,直到找到其祖先元素在同一级时停止。然后,再依次往下寻找各自的子元素,找到第一个是层叠上下文元素的子元素后进行比较。
子元素的 z-index
值只在父级层叠上下文中有意义。即父元素的z-index
低于父元素另一个同级元素,子元素 z-index再高也没用。
z-index
值为负的元素比较特殊,他们会先被绘制,意味着他们可以出现在其他元素的后面,甚至出现在它的父元素后面。但是必要条件是该元素必须与父元素处于同一层,并且父元素不是这个层的根元素。
罗里吧嗦的说了半天,不知道各位看官对于z-index失效的原因有没有搞明白?
如何让DOM元素去“触发”层叠上下文呢?根据 MDN的说法就是:
- 根元素 (HTML),
- z-index 值不为 "auto"的 绝对/相对定位,
- 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex,
opacity
属性值小于 1 的元素(参考 the specification for opacity),transform
属性值不为 "none"的元素,mix-blend-mode
属性值不为 "normal"的元素,filter
值不为“none”的元素,perspective
值不为“none”的元素,isolation
属性被设置为 "isolate"的元素,position: fixed
- 在
will-change
中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考 Everything You Need to Know About the CSS will-change Property)-webkit-overflow-scrolling
属性被设置 "touch"的元素
只有这些就算是理解了z-index
了么?开什么玩笑,没点干货敢出来写东西么?
我们写3个<div>
元素,然后每个<div>
元素里面都有一个<span>
元素,每个<span>
元素都有个背景色,并且使用absolute
定位,为了能更清楚地看到z-index
的效果,添加一些其他的样式。第一个<span>
元素的z-index
值为1
,其他两个没有设置。
<!--html-->
<div>
<span class="red">Red</span>
</div>
<div>
<span class="green">Green</span>
</div>
<div>
<span class="blue">Blue</span>
</div>
/*css*/
.red, .green, .blue {
position: absolute;
width: 100px;
color: white;
line-height: 100px;
text-align: center;
}
.red {
z-index: 1;
top: 20px;
left: 20px;
background: red;
}
.green {
top: 60px;
left: 60px;
background: green;
}
.blue {
top: 100px;
left: 100px;
background: blue;
}
我们会得到像下图一样的东西
好玩的来了:尝试把红色的<span>
元素放到其他两个元素后面,但是必须遵守下面的规则:
- 不能修改HTML的内容
- 不能增加或修改任何元素的
z-index
属性 - 不能增加或修改任何元素的
position
属性
有点难度么?还是觉得不可能呢?如果你已经找到了问题的解决方案,恭喜你看文章看的很仔细,知识细节的掌握很是完美,如果想了半天还是找不到办法的话,就先请自己的优越感和自信心收一收吧,我们接下来进行。
解决方案很简单,你只需要给红色的<span>
标签增加一个opacity
小于1
,像下面这样:
div:first-child {
opacity: .99;
}
其实在上文中DOM元素去“触发”层叠上下文的第四项就已经告诉了我们答案了啊。复习下上文介绍的层叠上下文等概念的知识,有助于我们总结为什么红色的<span>
会去到下面:
- 开始有两个层,一个由根节点产生,一个由设置了
z-index:1
并且position:absolute
的红色<span>
产生。- 当我们设置了
opacity
时,产生了第三个层,并且第三个层把红色<span>
产生的层包裹了,意味着刚开始的z-index的作用域只在第三个层里面。- 而所有的
<div>
都没有定位或者z-index
,所以他们的堆叠顺序按照HTML出现顺序排列,于是第三个层就去到下面。
报告完毕~