17

前言

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>

clipboard.png

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>

clipboard.png

根据顺序规则和定位规则, 我们可以做出更加复杂的结构。A和 B 都设为非定位节点,A 的子节点 A-1 设定定位节点。

<div id="a">
    <div id="a-1" style="position: relative">A</div>
</div>
<div id="b">B</div>

clipboard.png

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>

clipboard.png

我们将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>

clipboard.png

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>

clipboard.png

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>

clipboard.png

如果定位节点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>

clipboard.png

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>

clipboard.png
clipboard.png

下面这个更复杂的层级树,聪明的你能看明白为什么节点层级是这样的了吗?

<div id="a" style="position: relative">
    <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>

clipboard.png

层叠上下文

介绍了这么多规则,是在是不好理解,又很难记。其实,要理解这些规则,我们只需要理解一个概念就行了,它就是层叠上下文。

概念

层叠上下文是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)

没有角的牛
180 声望6 粉丝

It's never too late to learn.