深入理解z-index

简介: 要解决的问题 在页面编写的过程中,经常需要处理元素的重叠。重叠的顺序不当则容易造成元素被错误地遮盖等现象。一般地,有很多人认为只需要指定元素的z-index即可调整重叠的顺序,但是实际上并不是这样的。

要解决的问题

在页面编写的过程中,经常需要处理元素的重叠。重叠的顺序不当则容易造成元素被错误地遮盖等现象。一般地,有很多人认为只需要指定元素的z-index即可调整重叠的顺序,但是实际上并不是这样的。

重新理解页面维度

当我们打开一个网页,我们会看到一个二维的世界。在这个二维的世界里,页面里的box(盒子)各有自己的x坐标和y坐标。比如,在下图所示的页面结构里面,有四个div元素。它们分别具有自己的宽和高,而每个box左上角的x和y坐标就分别是这个box在页面中的x和y坐标。因而,在我们直观的感知里,页面是二维的。

8f1b4c1f013ada51e1810bc69026b1f3c2eadc83

然而,页面实际上还有第三个维度。垂直于x轴与y轴的方向,存在一个c轴。c轴的正方向指向用户。对于一个页面上的box,它一定有一个c坐标。注意,此处的c坐标并不是z-index。下图中的坐标名有误,z应该是c。

0800b3a63bafb86576008df954ab62a25176ce71

两个重叠的box最能证明这第三个维度的存在,如果页面上有两个元素是重叠的,那么浏览器的渲染引擎必须决定哪一个box的重叠部分要被放在前面。决定的方法很简单,就是直接比较这两个元素的c坐标。c坐标大的那一个,就被渲染在前面;反之,则被压在下面。

你不能把c坐标的大小理解成离用户的远近,因为如果那样理解,那么应该有“近大远小”现象。然而事实上,c坐标大的box并不会变小,只是被浏览器渲染在c坐标小的box前面而已。就好像在现实生活中,我们把两张卡片叠在一起,它们会有上下之分,但是看起来两张卡片的大小并不会有所改变(因为它们足够薄且小)。会产生近大远小现象的应该是z坐标,学过一点空间几何的人都应该熟悉。这也是我称这个维度为c坐标而非z坐标的原因。

下面我们一起来探究一下页面box之间是如何重叠的,换句话说,浏览器是怎么决定一个box的c坐标的。

默认重叠

对于重叠的元素,浏览器默认会按下面的顺序重叠。编号越大的box类型,所拥有的c坐标就越大,因此排在前面。

正常流当中的block level的box

浮动的元素

正常流当中inline level或者inline-block level的box

position值不是static(非正常流中)的box

这里并不是完整的列表,因为我略去了一些后面才会提到的内容。下面是一个示例:

4e036061c935dff85de0276c20a682cc7c3452dd

可以看到,上述四种不同类型的box中,具有最大c坐标的是第4种,它能够覆盖其他所有三种元素。

你需要注意的一点是,不要用DOM树的结构来理解重叠。DOM树中下一级的box完全可以重叠在上一级box之上。这都和Stacking Context有关,我们接下来就详细解释一下。

Stacking-Context

上述四种box类型的重叠规律,当且仅当这些box在同一个Stacking Context的时候生效。不同Stacking Context中的box之间的重叠在下面会提到。

什么是Stacking Context?假设你正在玩贴纸,将很多贴纸贴到同一张纸上,并且贴纸之间可能产生重叠。Stacking Context就像是这张纸。浏览器首先按照默认的重叠规律,将同一个Stacking Context下的所有元素排好顺序,然后按照这个顺序渲染到Stacking Context上。例如,在下面的DOM结构中,有5个box,其中node1是一个Stacking Context,其他的都不是。

d55c9c9c79df0e4665d0fd6f97f3b0a4654f51ae

在渲染的时候,浏览器将node1当做画板,其他box都是贴纸。在决定贴纸顺序的时候,浏览器是不会考虑它们DOM结构上的父子关系的。也就是说,node3-1完全可以被渲染在node2-1的后面,只要在前面所说的默认重叠顺序中,node3-1具有的c坐标比node2-1来得低即可。

整个“贴纸”的过程如下图。可以看到,浏览器在当前Stacking Context中,无视了“贴纸”们的DOM结构之间的关系,而是通过我们前面提到的默认重叠顺序决定“贴纸”的先后关系。决定之后,浏览器将所有“贴纸”贴到Stacking Context上,这个过程称作Composite(组合)。

c77b41631d29a01a03a7b3d20cb9804320513e15

什么样的元素是一个Stacking-Context

符合下面要求的页面box,都是一个Stacking Context。

根元素(html元素)

position是absolute或者relative,并且z-index不是auto的元素

是flex item,并且z-index不是auto的元素

opacity值小于1的元素

在mobile webkit以及Chrome 22+中,position: fixed的元素

多个Stacking-Context之间的重叠

由上面产生Stacking Context的条件,我们可以知道,一个页面上一般有多个Stacking Context。那么多个Stacking Context之间如何决定重叠顺序呢?浏览器一般按下面的规则来决定其顺序,这实际上就是之前我们提到的默认重叠顺序的完整版。

Stacking Context的背景和边框

具有负的z-index的,且position值不是static(非正常流中)的子box的Stacking Context,且z-index数值越小,其c坐标越小

正常流当中的block level的box

浮动的元素

正常流当中inline level或者inline-block level的box

position值不是static(非正常流中)的box,且z-index为0或者auto

具有正的z-index的,且position值不是static(非正常流中)的子box的Stacking Context,且z-index数值越小,其c坐标越小

你需要注意到的是,z-index只能作用在position值不是static的box上方能起效。下面的例子可以说明多个Stacking Context之间的重叠规律。

b3944987873271344fa95b9eb523f8f8c947876f

在这里,.wrapper、#b1和#b2分别都是一个Stacking Context。#b1和#b2是.wrapper的子Stacking Context,浏览器会首先组合#b1以及组合#b2,之后再将#b1和#b2组合到.wrapper上。由于#b1具有正的z-index,而#b2具有负的z-index,所以#b1被组合到了#b2的上面。

你可以试着把#b1的z-index改成-2,那么它就变成了第二类的box(和#b2一样),又因为它的z-index比#b2来得小,所以它会被组合到#b2之后。

总结

z-index只在同一个Stacking Context的组合过程中,参与各个子box的重叠顺序的决定。但是页面box的重叠关系并非仅仅和z-index有关。清楚地认识z-index的工作原理,有助于你写出更有效率的样式表。

原文发布时间为:2018-10-11

原文作者:桃翁

本文来自云栖社区合作伙伴“前端桃园”,了解相关信息可以关注“前端桃园”。

相关文章
|
5天前
|
数据库 数据库管理 索引
DROP INDEX
【11月更文挑战第16天】
12 2
|
4月前
|
存储 关系型数据库 MySQL
第8章 索引index
第8章 索引index
33 0
|
6月前
|
索引 Python
row[i] = col[j] = TrueIndexError: list assignment index out of range
row[i] = col[j] = TrueIndexError: list assignment index out of range
|
C# 索引
C# index of 用法(转载)
查找字串中指定字符或字串首次出现的位置,返首索引值,如:  str1.IndexOf("字"); //查找“字”在str1中的索引值(位置) str1.IndexOf("字串");//查找“字串”的第一个字符在str1中的索引值(位置) str1.IndexOf("字",start,end);//从str1第start+1个字符起,查找end个字符,查找“字”在字符串S
1291 1
|
SQL 索引
ORA-01502: index ‘index_name' or partition of such index is in unusable state
错误现象:   今天发布脚本时,一个表插入数据时报如下错误   ORA-01502: index ‘index_name' or partition of such index is in unusable state   ORA-06512: at line 168 错误原因:   这个错误一般是因为索引状态为UNUSABLE引起的。
984 0
|
Web App开发 前端开发