现象
起因是有一个需求,要做出有渐变色的阴影。那我们知道,要做阴影可以直接用css3的box-shadow
属性,非常方便。但是box-shadow
的阴影颜色是只支持rgba的,并不支持渐变效果,所以渐变色的阴影不能直接用box-shadow
来做。所以我就换了个方法,用after伪类(或者一个子节点)自己来画阴影。那么快速的画出这个阴影
第一种
<--html-->
<div class="node"></div>
<--css-->
.node {
position:relative;
background:green;
width:100px;
height:100px;
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
}
然后看下效果
很明显,伪类或者子节点,默认肯定是在父节点上面的,那能不能用z-index
把他沉下去呢?第一次尝试,把父节点的z-index
设为2,子节点设为1。
第二种
.node {
position:relative;
background:green;
width:100px;
height:100px;
z-index:2
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
z-index:1;
}
看下效果
并没有任何变化,是子节点的z-index
不够小吗?那么把子节点的z-index
设为负值呢
第三种
.node {
position:relative;
background:green;
width:100px;
height:100px;
z-index:1
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
z-index:-1;
}
再看下效果
还是没有变化。现在把父节点的z-index
去掉或者设为auto
。
第四种
.node {
position:relative;
background:green;
width:100px;
height:100px;
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
z-index:-1;
}
再看看效果
子节点终于到了父节点下面,也就是说,只有当父节点没有z-index
或者z-index
为auto
,且子节点z-index
为负值时,子节点才会到父节点的下面。
事情还没有结束,如果把这个带阴影的组件放到一个新的容器中去,像这样。
第五种
<--html-->
<div class="parent">
<div class="node">
</div>
</div>
<--css-->
.parent {
background:red;
width:200px;
height:200px;
}
看下效果
发现由于阴影的z-index
是负值,所以它甚至在parent
节点的下面,这就不是我想要的了。当然,这时候你可以把parent
节点的z-index
设为-2,让它去更下面,但这又带来了另一个问题,假如node节点上有点击事件,会因为这个z-index=-2
而失效。
第六种
<--html-->
<div class="parent">
<div class="node">
<a onclick="alert(1)">click</a>
</div>
</div>
<--css-->
.parent {
background:red;
width:200px;
height:200px;
position:relative;
z-index:-2;
}
.node {
position:relative;
background:green;
width:100px;
height:100px;
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
z-index:-1;
}
这样做,虽然ui上符合要求,阴影是正确的显示了,但是点击click并不会有任何效果。要让这个阴影组件正确显示且能进行交互,需要把parent
节点的z-index
设为1。
第七种
<--html-->
<div class="parent">
<div class="node">
<a onclick="alert(1)">click</a>
</div>
</div>
<--css-->
.parent {
background:red;
width:200px;
height:200px;
position:relative;
z-index:1
}
.node {
position:relative;
background:green;
width:100px;
height:100px;
}
.node::after {
content:'';
width:100px;
height:100px;
position:absolute;
background:linear-gradient(90deg,#4f5aff 0,#4242e1 100%);
top:50px;
left:50px;
z-index:-1;
}
这样,这个阴影组件就可以正常显示,并且不影响交互。
另外,如果在parent
节点中加上一个slot
节点的兄弟节点other
,slot和other的显示关系就是普通的z-index
比大小。
结论
最终通过这样的四层父子节点的结构,可以得出结论:
- 父子节点之间,子节点默认在父节点之上,除非子节点的
z-index
为负且父没有z-index
(或为auto
) - 为了让
z-index
为负的节点只在他的父节点之下,而不在更外层的节点之下,需要给父节点再套上一层z-index>=0
的节点 - 兄弟节点之间还是直接比较
z-index
的大小,如果一样就比先后顺序。
原理
在w3c的文档中,关于z-index
的描述里,有一个叫做堆栈上下文(stacking contexts)的概念,在同一个堆叠上下文中,元素按照一定的顺序进行堆叠。文档中的顺序为(从下到上)
- 构成堆栈上下文的根元素。
- 堆栈级别为负的子元素(小的优先)。
- 非行内、没有
position
的元素。 - 没有
position
的float
元素。 - 行内、没有
position
的元素,包括table和block。 - 堆栈级为0的子堆栈上下文和堆栈级别为0的
position
元素。 - 堆栈级大于0的子堆栈上下文(小的优先)。
而关于什么时候会产生新的堆栈上下文,文档中给了三种情况。
- 根元素(html)
- 任何有定位且
z-index
不为auto
的元素 - 未来的css中,一些其他的属性也可能引入堆栈上下文,例如不透明度(
opacity
)
再来回头看看之前的几个例子。
第一种情况,没有z-index
,都在同一个堆栈上下文中,那就是按照父子关系进行堆叠。
第二种和第三种情况类似,父节点的z-index
设为2,子节点为1或者-1。此时,父节点已经产生了一个新的堆栈上下文,并且父节点是这个上下文的根元素,所以不管子节点有没有z-index
,一定在上面。
第四种情况,父节点的z-index
为auto
,子节点为负。此时父子节点在同一个堆叠上下文中,并且根元素是html,父节点不是根元素,根据堆叠顺序,堆叠级别为负的元素在下。
第五种情况和第四种情况类似,parent
节点和node
节点情况相同,在同一个堆栈上下文忠且都不是根元素,所以都在子节点上面。
第六种情况和第七种情况类似,只要给parent
节点加上一个z-index
,就产生了新的堆栈上下文,且parent
节点是这个上下文的根元素,所以会在最下面。至于这个z-idnex
是正是负,对于堆叠顺序并没有影响,设为任意非负值就可以。
同样的,由于opacity
也可以产生新的堆栈上下文,所以给6、7中的parent
节点加上一个opacity:0.99
的属性,也可以达到一样的效果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。