前言
DOM层级顺序,大概来说就是DOM节点在z轴方向(垂直于屏幕向外的方向)的显示优先级。为了调整DOM层级顺序,我们想到的往往就是用CSS的z-index属性来控制。虽然看着很简单,但是有时在使用时,我们有时也许会碰到一些一些奇奇怪怪的问题:
- 为什么有时候将z-index设成很大的值(比如9999),节点依然不能显示在最前面?
- z-index属性设为0和不设置z-index属性有什么区别?
- float元素和定位元素谁显示的优先级更高?
其实看似简单的层级顺序它自己的一套规则,理解这些规则可以帮助我们在开发中少踩坑。
规则
1. 顺序规则
在不设置position属性(或设置成static)的情况下,文档流后面的DOM节点会覆盖前面的DOM节点。
<div id="a">A</div>
<div id="b">B</div>
PS:怎么样来让文档流中的节点叠在一起呢?比如我们可以设置DOM的margin-top
为负数,这样就让两个节点叠在起一起了。不过为了简化说明,并没有把这些写出来。下同。
2.定位规则
定位节点(position属性设置为relative,absolute或fixed的节点)会覆盖非定位节点(不设置position属性或position属性设为static的节点)
<div id="a" style="position: relative">A</div>
<div id="b">B</div>
根据顺序规则和定位规则, 我们可以做出更加复杂的结构。A和 B 都设为非定位节点,A 的子节点 A-1 设定定位节点。
<div id="a">
<div id="a-1" style="position: relative">A</div>
</div>
<div id="b">B</div>
3. 参与规则
z-index属性仅对定位节点生效。
有三个DOM节点,其定位为static。但是A的z-index最大,但是依旧是在最底部,C的z-index最小,而在最顶部,因此可知z-index并未生效,此时为顺序规则在生效。
<div id="a" style="z-index: 2">A</div>
<div id="b" style="z-index: 1">B</div>
<div id="b" style="z-index: 0">B</div>
我们将B和C的position设置为relative之后,顺序发生了变化。根据参与者规则和定位规则,A不是定位节点,所以即使z-index最大,还是在最底部。而根据参与规则和默认值规则(下一节介绍),B和C都是定位节点,且B的z-index要大于C,所以B在最顶部。
<div id="a" style="z-index: 2">A</div>
<div id="b" style="position: relative; z-index: 1">B</div>
<div id="b" style="position: relative; z-index: 0">B</div>
4. 默认值规则
- 对于所有的定位节点,z-index值大的节点会覆盖z-index值小的节点。
- z-index设为0和没有设置z-index的节点在同一层级内没有高低之分。在IE6和7种,z-index的默认值为0,其他浏览器中默认值为auto。
<div id="a" style="position: relative; z-index: 1">A</div>
<div id="b" style="position: relative; z-index: 0">B</div>
<div id="c" style="position: relative">C</div>
<div id="d" style="position: relative; z-index: 0">D</div>
5. 从父规则
两个节点A和B都是定位节点,如果节点A的z-index值比节点B的大,那么节点A的子元素都会覆盖在节点B以及节点B的子节点上。
<div id="a" style="position: relative; z-index: 1">
<div id="a-1">A-1</div>
</div>
<div id="b" style="position: relative; z-index: 0">
<div id="b-1">B-1</div>
</div>
如果定位节点A和B的z-index值一样大,那么根据顺序规则,B会覆盖A,那么即使A的子节点的z-index比B的子节点大,B的子节点还是会覆盖A的子节点。(这就是为什么即使我们把A-1的z-index设置得很大,依然无法盖住B节点的原因)。
<div id="a" style="position: relative; z-index: 0">
<div id="a-1" style="position: relative; z-index: 2">A-1</div>
</div>
<div id="b" style="position: relative; z-index: 0">
<div id="b-1" style="position: relative; z-index: 1">B-1</div>
</div>
6. 层级树规则
定位节点,且z-index有整数值的(不包括z-index:auto),会被放置到一个与DOM节点不一样的层级树里。
在下面的DOM节点中,A和B是兄弟节点,但是在层级树种,A和B-1才是兄弟节点(因为他们都是Root下的第一层含有整数z-index的定位节点),所以A在B和B-1之上。虽然A-1的z-index比B-1的小,但是根据从父规则,A-1也在B-1之上。
<div id="a" style="position: relative; z-index: 2">
<div id="a-1" style="position: relative; z-index: 0">A-1</div>
</div>
<div id="b">
<div id="b-1" style="position: relative; z-index: 1">B-1</div>
</div>
下面这个更复杂的层级树,聪明的你能看明白为什么节点层级是这样的了吗?
<div id="a" style="position: relative; z-index: 2">
<div id="a-1" style="position: relative; z-index: 100">A-1</div>
</div>
<div id="b">
<div id="b-1" style="position: relative">
<div id="b-1-1" style="position: relative; z-index: 10">B-1-1</div>
</div>
</div>
<div id="c" style="position: relative">
<div id="c-1">
<div id="c-1-1">
<div id="c-1-1-1" style="position: relative; z-index: 1">C-1-1-1</div>
</div>
</div>
</div>
层叠上下文
介绍了这么多规则,是在是不好理解,又很难记。其实,要理解这些规则,我们只需要理解一个概念就行了,它就是层叠上下文。
概念
层叠上下文是html元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。
特性
- 子元素的 z-index 值只在父级层叠上下文中有意义。
- 子级层叠上下文被自动视为父级层叠上下文的一个独立单元。
产生的条件
满足一下其中一个条件,即可产生一个层叠上下文:
- 根元素 (HTML),
- z-index 值不为 "auto"的 绝对/相对定位,
- position: fixed
- opacity 属性值小于 1 的元素
- transform 属性值不为 "none"的元素
- filter值不为“none”的元素
- -webkit-overflow-scrolling 属性被设置 "touch"的元素
附录:层叠的顺序表
位置 | 描述 | CSS |
---|---|---|
1(底部) | 包含该层叠上下文的父元素 | |
2 | 负堆叠顺序的子元素 | z-index: <negative integer>; position: relative (or absolute or fixed) |
3 | 文档流中,非内联,非定位子元素 | display: /* not inline */; position: static |
4 | 非定位浮动子元素 | float: left (or right); position: static |
5 | 内联流,非定位子元素 | display: inline; position: static |
6 | 堆叠顺序为0的子元素 | z-index: auto (or 0); position: position: relative(or absolute or fixed) |
7(顶部) | 堆叠顺序为正的子元素 | z-index: <positive integer>; position: relative(or absolute or fixed) |