CSS z-index 属性的使用方法和层级树的概念

Nov 27th, 2011 Add Comment

CSS 中的 z-index 属性用于设置节点的堆叠顺序, 拥有更高堆叠顺序的节点将显示在堆叠顺序较低的节点前面, 这是我们对 z-index 属性普遍的认识. 与此同时, 我们总是对堆叠顺序捉摸不透, 将 z-index 的值设得很大也未必能将节点显示在最前面. 本文将通过一些例子对 z-index 的使用方法进行分析, 并且为各位带入 z-index 层级树的概念.

这个星期我们团队做了一次内部的技术分享, 南瓜小米粥为我们分享了他对 CSS z-index 的理解和引入层级树这个概念. 这个分享的现场效果很好, 所以我也将 z-index 和层级树话题搬到博客来谈一谈.

本文谈及多个影响节点显示层级的规则, 其中用到的所有例子全部罗列在《position 属性和 z-index 属性对页面节点层级影响的例子》中.

目录

顺序规则

如果不对节点设定 position 属性, 位于文档流后面的节点会遮盖前面的节点.

<div id="a">A</div>
<div id="b">B</div>

CSS z-index 属性顺序规则的例子

定位规则

如果将 position 设为 static, 位于文档流后面的节点依然会遮盖前面的节点浮动, 所以 position:static 不会影响节点的遮盖关系.

<div id="a" style="position:static;">A</div>
<div id="b">B</div>

CSS z-index 属性定位规则的例子, static

如果将 position 设为 relative (相对定位), absolute (绝对定位) 或者 fixed (固定定位), 这样的节点会覆盖没有设置 position 属性或者属性值为 static 的节点, 说明前者比后者的默认层级高.

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

CSS z-index 属性定位规则的例子, relative | absolute | fixed

在没有 z-index 属性干扰的情况下, 根据这顺序规则和定位规则, 我们可以做出更加复杂的结构. 这里我们对 A 和 B 都不设定 position, 但对 A 的子节点 A-1 设定 position:relative. 根据顺序规则, B 会覆盖 A, 又根据定位规则 A' 会覆盖 B.

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

CSS z-index 属性互相覆盖的例子

上面互相覆盖在什么时候用到这样的实现? 看起来偏门, 其实很常用, 比如说, 电子商务网站侧栏的类目展示列表就可以用这个技巧来实现.

下图是某网站的类目展示区域, 二级类目的悬浮层覆盖一级类目列表外框, 而一级类目的节点覆盖二级类目的悬浮层. 如果使用 CSS 实现展示效果, 一级类目的外框相当于上面例子中的 A, 一级类目的节点相当于 A-1, 二级类目的悬浮层相当于 B.

电子商务网站侧栏的类目展示列表

参与规则

我们尝试不用 position 属性, 但为节点加上 z-index 属性. 发现 z-index 对节点没起作用.

<div id="a" style="z-index:2;">A</div>
<div id="b" style="z-index:1;">B</div>
<div id="c" style="z-index:0;">C</div>

CSS z-index 属性参与规则的例子, 没有明确定位的时候

W3C 对 z-index 属性的描述中提到 在 z-index 属性仅在节点的 position 属性为 relative, absolute 或者 fixed 时生效.

The z-index property specifies the stack order of an element. Only works on positioned elements(position: absolute;, position: relative; or position: fixed;).

<div id="a" style="z-index:2;">A</div>
<div id="b" style="position:relative;z-index:1;">B</div>
<div id="c" style="position:relative;z-index:0;">C</div>

CSS z-index 属性参与规则的例子, 明确定位的节点才能使用 z-index 属性

默认值规则

如果所有节点都定义了 position:relative. z-index 为 0 的节点与没有定义 z-index 在同一层级内没有高低之分; 但 z-index 大于等于 1 的节点会遮盖没有定义 z-index 的节点; z-index 的值为负数的节点将被没有定义 z-index 的节点覆盖.

<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>

CSS z-index 属性默认值规则的例子

通过检查我们还发现, 当 position 设为 relative, absolute 或者 fixed, 而没有设置 z-index 时, IE8 以上和 W3C 浏览器 (下文我们统称为 W3C 浏览器) 的 z-index 默认值是 auto, 但 IE6 和 IE7 是 0.

从父规则

如果 A, B 节点都定义了 position:relative, A 节点的 z-index 比 B 节点大, 那么 A 的子节点必定覆盖在 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>

CSS z-index 属性从父规则的例子, 子节点不设定层级

如果所有节点都定义了 position:relative, A 节点的 z-index 和 B 节点一样大, 但因为顺序规则, B 节点覆盖在 A 节点前面. 就算 A 的子节点 z-index 值比 B 的子节点大, B 的子节点还是会覆盖在 A 的子节点前面.

<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>

CSS z-index 属性从父规则的例子, 不可逾越的层级

很多人将 z-index 设得很大, 9999 什么的都出来了, 如果不考虑父节点的影响, 设得再大也没用, 那是无法逾越的层级.

层级树规则

可能你会觉得在 DOM 结构中的兄弟节点会拎出来进行比较并确定层级, 其实不然.

<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>

CSS z-index 属性层级树规则的例子

我们认为同时将 position 设为 relative, absolute 或者 fixed, 并且 z-index 经过整数赋值的节点, 会被放置到一个与 DOM 不一样的层级树里面, 并且在层级树中通过对比 z-index 决定显示的层级. 上面的例子如果用层级树来表示的话, 应该如下图所示.

CSS z-index 的层级树

图中虽然 A-1 (z-index:0) 的值比 B-1 (z-index:1) 小, 但因为在层级树里 A (z-index:2) 和 B-1 在一个层级, 而 A 的值比 B-1 大, 根据从父规则, A-1 显示在 B-1 前面.

参与规则 2

前面提到的参与规则认为只要节点的 position 属性为 relative, absolute 或者 fixed, 即可参与层级比较, 其实不准确. 如果所有节点都定义了 position:relative, 并且将 z-index 设为整数值, 根据从父规则, 父节点的层级决定了子节点所在层级.

<div id="a" style="position:relative;z-index:0;">
	<div id="a-1" style="position:relative;z-index:100;">A-1</div>
</div>
 
<div id="b">
	<div id="b-1" style="position:relative;z-index:0;">
		<div id="b-1-1" style="position:relative;z-index:10;">B-1-1</div>
	</div>
</div>
 
<div id="c" style="position:relative;z-index:0;">
	<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>

例子中 A, B-1, C-1-1 作为父节点, z-index 的值相同, 根据顺序规则, C-1-1 在 B-1 之前, B-1 在 A 之前; 又根据从父规则, 无论子节点的 z-index 值是什么, C-1-1-1 在 B-1-1 之前, B-1-1 在 A-1 之前.

CSS z-index 属性参与规则 2 的例子, 所有节点参与层级比较

如果我们将所有父节点的 z-index 属性去除, 诡异的事情发生了. IE6 和 IE7 浏览器显示效果不变, 而 W3C 浏览器的子节点不再从父, 而是根据自身的 z-index 确定层级.

<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>

根据默认值规则, IE6 / IE7 和 W3C 浏览器上的元素存在 z-index 默认值的区别. 我们相信, 仅当 position 设为 relative, absolute 或者 fixed, 并且 z-index 赋整数值时, 节点被放置到层级树; 而 z-index 为默认值时, 只在 document 兄弟节点间比较层级. 在 W3C 浏览器中, A, B-1 和 C-1-1 的 z-index 均为 auto, 不参与层级比较.

CSS z-index 属性参与规则 2 的例子, z-index 为 auto 的节点不参与层级比较

而在 IE6 和 IE7 中, 因为 z-index 的默认值是 0, 所以也参与了层级比较.

CSS z-index 属性参与规则 2 的例子, IE6 和 IE7 中 z-index 默认为 0

设置了 position 而没有 z-index 的节点虽然不参与层级树的比较, 但还会在 DOM 中与兄弟节点进行层级比较.

<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">
		<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>

我们对上个例子改造一下, 将 B-1 的 position 属性删除后, W3C 浏览器显示如下图. 根据定位规则, A 和 C-1-1 会显示在 B-1 的前面; 而根据顺序规则, C-1-1 又显示在 A 前面.

CSS z-index 属性参与规则 2 的例子, position 为 auto 的节点不参与层级树比较, 但仍参与 DOM 兄弟节点间的层级比较, W3C 浏览器

在 IE6 和 IE7 中, 因为 A 和 C-1-1 设置了 position:relative, 而且 z-index 的默认值为 0, 所以也参与层级树比较, 所以有如下效果.

CSS z-index 属性参与规则 2 的例子, position 为 auto 的节点不参与层级树比较, 但仍参与 DOM 兄弟节点间的层级比较, IE6 和 IE7

总结

浏览器节点显示层级是一个费力的活, 今天你觉得 A 区块会永远置顶, 但明天因为需求变动, 突然出现 B 元素需要置顶. 一层一层往上堆砌, 某天回头一看, 发现很多区块交错在一起, 而且他们的值一个比一个大, 根本搞不清头绪. 我觉得在操刀干活之前, 最好先将 position, z-index 和层级的关系搞搞清楚, 以免后患无穷.

另外, 非情非得已, 切勿用 JavaScript 计算 z-index, 并将某个节点的 z-index 设置成所有节点中层级最高.

因为篇幅太长, 本文仅从节点属性角度进行讨论, 没有涉及 select 和 iframe 等特殊页面节点考虑, 如果有机会下次再为大家分享.

声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: CSS z-index 属性的使用方法和层级树的概念

  1. http://0.gravatar.com/avatar/4b400432b81f80f6ff19f0c654f53423?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    请教大侠,我有同一个级别的若干个div,position都是absolute,没有设置z-Index,互相没有遮盖。点击某个div的时候把他的z-Index设为100,松开再设为'auto'。现在的问题是,当我点某个div之后,它后面的所以div都不能点了,但如果从后面往前一个一个点击是可以的。而改为松开后z-Index设为1就没问题了,不知道是什么原因,望指点!!!

  2. http://0.gravatar.com/avatar/04b3e9066e014db0dddf939aec19b7a0?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    学习了,平时虽然有注意,但是这篇文章收集的情况都分得很详细

  3. http://0.gravatar.com/avatar/e02c2721d8275fc6f2e3225105ee30fa?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    今天第一次用手机上neoease 手机上的感觉也不错诶

  4. http://0.gravatar.com/avatar/25a1cbed2e4e537d811c0571bfc9fc8d?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    这是我见过写的最详细的!!!!简直是神作!

  5. http://0.gravatar.com/avatar/0ff6717e4da299a30c40d2d6c7e0fe7b?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    强烈推荐!!!

  6. http://0.gravatar.com/avatar/68e9b46c69ffdeeae1e7fea66227affc?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    分析的好仔细,赞!

  7. http://1.gravatar.com/avatar/f47890bf729664a08a060d7f832320a1?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    楼主,学习了,作为设计的学生来说,这方面是弱项,好好学习了

  8. http://0.gravatar.com/avatar/696db72a052e991a3c71ec7707e5ce4a?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    确实好复杂啊。

  9. http://1.gravatar.com/avatar/db5758e716572f2bebb4c371320da506?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G
  10. http://0.gravatar.com/avatar/25a1cbed2e4e537d811c0571bfc9fc8d?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    这次我是真懂了 楼主我爱死你了

  11. http://0.gravatar.com/avatar/82916ebcb66ae50b66ce8437590df114?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    写的太棒了,好好学习下。

  12. http://0.gravatar.com/avatar/ceb8f6857d708a792e5ac65c8e734bbf?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    过于专业,没看懂,看来我真不行

  13. http://1.gravatar.com/avatar/91fde6aef899458f1c3b1fd1678e9c23?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    讲得很详细。其实我觉得只要充分理解position为relative,absolute,fixed时是将对应节点从DOM里抽出来,相应的z-index也就容易理解了

  14. http://0.gravatar.com/avatar/eba80d6e4d3a3ffa491fe3a83b0cf56c?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    这些讲的都是一些CSS的知识吧,层是很重要。

  15. http://0.gravatar.com/avatar/490cf262668eebb0f0f1a50d9d48d702?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @Leo
    不是 100% 重现, IE 历代都是灵异事件的集聚地. 后头我试一下.

  16. http://0.gravatar.com/avatar/c3bbb9ae30a86d6adbb98f71874a0b07?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @mg12
    可能我没说清楚……就是DOM里一个节点下的子元素,比如UL里面的几个LI,按照你的这个讲法,应该是层级树里面完全同一等级的节点。如果这些LI的z-index都大于0,就会很正常;如果有一个LI的z-index等于0,它有可能会跑到其他z-index大于0的LI的上面,不过也不是100%重现。

  17. http://0.gravatar.com/avatar/4fb7761ca11b63a3b4ed76090d5ffe73?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    说的很好,支持!感谢分享!

  18. http://1.gravatar.com/avatar/7864d250ba3b93e42b4f68259881440b?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    学习了,谢谢分享!

  19. http://0.gravatar.com/avatar/e8f87528ed0a0eaba60009f8580df401?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    这个必须保存...

  20. http://0.gravatar.com/avatar/490cf262668eebb0f0f1a50d9d48d702?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @Leo
    DOM 里面同一层级没用啊, 需要层级树里一样.

    @wmtimes
    淘宝的标题的 position 是 static 吧?

  21. http://0.gravatar.com/avatar/e0e6d9f3b7c71e1a4a1a21fee9106f1d?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    囧,又是IE跑偏…… 这两天正在郁闷轮播的控制按钮为啥上不来,很受用! 文章很详细,可以当做z-index的手册来查询了~ 收藏之~ ^_^

  22. http://1.gravatar.com/avatar/799742820a8d3aad13a0b1763233b8ef?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    CSS我一直搞的就头大。

  23. http://0.gravatar.com/avatar/2667e3794c00b147ea9cda10b88365ae?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    这个学习了。我说淘宝的标题为什么用position呢,但还同时设置了margin他padding,又予以何为呢?

  24. http://0.gravatar.com/avatar/c3bbb9ae30a86d6adbb98f71874a0b07?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @mg12
    同一层级的absolute元素,z-index:0的有时候会跑到z-index大于0的上面去,只在IE9上碰到过……

  25. http://1.gravatar.com/avatar/3f6f95fb9a3a226a5025dc961e3957d3?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    毫不犹豫的收藏

  26. http://0.gravatar.com/avatar/490cf262668eebb0f0f1a50d9d48d702?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @Leo
    IE9 应该跟 Chrome 和 Firefox 等浏览器一样的啊.

  27. http://0.gravatar.com/avatar/253b4007557539ce0e443809f108017c?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    受教了,一直疑惑着,今天终于一知半解了,哈哈。
    有图有真相啊,有木有?

  28. http://1.gravatar.com/avatar/313d0583bfc8ccc7bebd7594f8497177?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    css的层级管理太复杂了,还是Flash的好管理,嘿嘿

  29. http://0.gravatar.com/avatar/c3bbb9ae30a86d6adbb98f71874a0b07?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    上礼拜我也帮同事修了一个z-index引起的bug,不得不说,IE对z-index的管理真糟糕,到版本9都还是那样……

  30. http://0.gravatar.com/avatar/076adff9eccb35fc4540386e4a987116?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    晕。。。看的太晕了。。。技术的东东

  31. http://0.gravatar.com/avatar/0e99ef2ad07f02eee35584d7a9263696?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    又见经典技术文。过来顶一下。

  32. http://1.gravatar.com/avatar/74d6c128298541e6a4e814362c7e5b18?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    博主真是辛苦了!

  33. http://1.gravatar.com/avatar/1cfd6b67e61a959b431aa1bc3e4f6736?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    顶个,难得的长篇技术分享~

  34. http://0.gravatar.com/avatar/e02c2721d8275fc6f2e3225105ee30fa?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    收藏了慢慢看