你是否碰到过这种情况:两个 div
都是 fixed
定位,但 z-index
值小的反而可以盖在 z-index
值大的上面。
position:fixed 和 z-index
首先明确以下两个属性:
1、position:fixed;
根据 MDN 文档上的说法
fixed
元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed
属性会创建新的层叠上下文。当元素祖先的transform
,perspective
或filter
属性非none
时,容器由视口改为该祖先。
2、z-index
只在定位元素上有效,即 position
的值不为 static
时,而且 z-index
的值越大,元素的位置越靠前。
参考链接:https://developer.mozilla.org/zh-CN/docs/Web/CSS/z-index
所以当我们在一个 div
上使用 position:fixed
时,只要把 z-index
的值设置得越大,显示出来时就越靠前(不被遮挡)。
以上就是我在此之前的理解。
不一样的情况出现了
今天碰到了这种情况,两个 div
都是 fixed
定位,但是一个 z-index:21;
的元素,把一个 z-index:1001;
的元素给挡住了。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.a {
width: 100vw;
height: 100vh;
}
.b {
position: relative;
z-index: 20;
width: 100%;
height: 70%;
background-color: antiquewhite;
}
.c {
position: fixed;
z-index: 1001;
left: 30%;
top: 30%;
width: 30%;
height: 30%;
background-color: aquamarine;
}
.d {
position: fixed;
z-index: 21;
width: 30%;
height: 30%;
left: 40%;
top: 40%;
background-color: red;
}
</style>
</head>
<body>
<div class="a">
<div class="b">
b ---- z-index: 20;
<div class="c">c ---- z-index: 1001;</div>
</div>
</div>
<div class="d">d ---- z-index: 21;</div>
</body>
</html>
效果:
a 包含 b,b 包含 c,而 d 与 a 同级,c 和 d 都设置 position: fixed;
其中 c 的 z-index 为1001,而 d 的 z-index 为21,原本期望的结果是 c 在 d 的上面,如下图所示:

而实际运行出来的结果是 d 在 c 的上面。

分析原因
经过分析,问题出在 b 上面,在 b 上面设置了属性 position: relative;
和 z-index: 20;
,当设置了这两个属性之后,在 b 上面就会创建一个层叠上下文,而 c 又是 b 的子元素,所以即使在 c 上面设置 z-index:1001;
,它也只是在 b 以内有效,超出 b 元素范围后,c 的 z-index 值是没有意义的;
b 元素的 z-index
值为20,它的参考对象是根元素 <html>
,d 的 z-index
值为21,参考对象也是根元素 <html>
,所以 d 会覆盖在 b 上面,而 c 又是 b 的子元素,无法突破 b,所以 c 也只能被 d 覆盖。
层叠上下文
产生上面这个问题的根本原因就是层叠上下文,所以弄清楚层叠上下文才是关键,请先仔细阅读 MDN 上的文档:层叠上下文。
在我们使用 z-index
属性时,一定要考虑层叠上下文,要清楚当前元素设置 z-index
之后,究竟是在哪个层叠上下文中起作用的,换句话说就是以哪个元素作参考的。
产生层叠上下文的条件有十几种,文档中的元素只要满足其中一个,就会在当前元素(起名叫 father)上形成层叠上下文,而 father 元素的所有子元素,如果设置了 z-index
,它们在渲染时,参考的就是 father 元素,谁的 z-index
值大,谁就在前面,但即使给这些子元素设置的 z-index
值再高,当超出 father 元素之后,他们的 z-index
就没有用了,因为这些子元素和其它元素已经不在一个层级上了。至于能不能显示在前面,就要看 father 元素的 z-index
了。
可以拿中国的省、地级市、直辖市来类比:
重庆是一个直辖市,在行政地位与江苏省同级,而苏州只是江苏省下面的一个地级市,假如苏州市2020年的GDP超过了重庆市(都是假如,不要抬杠),在江苏省内开会的时候,苏州可以排在最前面,但是如果开全国性的会议时,重庆可以和江苏省坐一排,而苏州只能坐在后面,因为级别不同。
这里全国就好比是 <html>
根元素,江苏省是一个层叠上下文,重庆也是一个上下文,GDP 就像是 z-index
值。江苏省和重庆在同一个层级中,都是省级行政单位,而苏州只是江苏省这个层叠上下文中的一个子元素,即使苏州的 GDP 超过了重庆,也还是一个地级市。
搞清楚层叠上下文,在安排元素显示层级时特别有用,还能避免一起意料之外的情况发生,你就能明白,为什么 Element-UI 中有些组件,比如 Dialog、Drawer 这类弹框组件,会有一个 append-to-body
属性来解决层级问题。
网友评论