大家好,我是念念!在开始文章之前,有两个面试真题:z-index值大的元素一定在值小的元素之上吗?如何实现父元素覆盖子元素?先公布答案:z-index不一定生效,也不一定上面有很大的值,主要看stackingcontext;为父元素设置一个大的z-index不能覆盖子元素,但是把子元素的z-index设置为负数却可以满足要求。这两道题的考点是级联上下文,本文会解释原因。文章首发于我的公众号:前端私教念念。订阅第一时间获取我的最新文章!顾名思义,层叠上下文就是从三维的角度,通过不同层级的元素放置来判断最终的层叠关系。它通过属性z-index确定“级别”。如何让z-index生效z-index用于指定元素在z轴上的高度,值越大,越接近用户,越“上”。在使用的时候,你可能会觉得这个属性不是很听话:给元素设置的z-index好像没有生效,并没有像预期的那样在其他元素上运行。因为单独使用不生效,所以必须和positioning属性一起使用,即只对指定position属性的元素生效——只要不是默认值static即可,其他绝对,relative和fixed可以使z-index有效。如图1所示,在粉红色的父元素下,有两个绝对定位的子元素1和2,它们都没有设置z-index,它们的位置由top/left属性控制,因此重叠。可以看到2在1之上,因为两者都没有设置z-index,所以可以认为堆叠层级为0,同一层级的元素会按照在层中出现的先后顺序来决定堆叠结果HTML。(图1)公众号后台回复103获取在线代码地址如果我们想让1在2之上,如图2,可以在元素1上加上z-index:1,元素2不指定z-indexz-index还是可以看成0的。根据大小关系,元素1在元素2之上。(图2)公众号后台回复104获取线上代码地址至此,没有任何困难,但这只是尺寸比较。但在很多情况下,会发现级联结果并不能用简单的数值来解释:如图3所示,粉色背景下有两个子元素1和2,而在粉色背景下有一个子元素32、三个元素绝对定位,元素3的z-index值最大,但是压在了元素1之下。(图3)公众号后台回复105获取在线代码地址,稍作修改,只去掉2号元素的z-index。如图4,元素3又上来了,覆盖了元素1和元素2。(图4)公众号后台回复106获取在线代码地址要理解这些问题,需要理解级联上下文。什么是堆叠上下文?堆叠上下文听起来很抽象。你可以把它想象成一个三维空间,里面有很多平面。最大的堆栈上下文是由文档的根元素——html构成的:它和它的子元素构成了最大的堆栈上下文,也就是说我们写的所有代码都在根堆栈上下文中。stackingcontext包含多个平面,具体来说:每个z-index值构成一个平面,普通未定位的块级元素也是一个平面,浮动元素也是一个平面,正是这些平面构成了stackingcontext。此外,每个具有z-index值的元素还会与其子元素一起生成一个小的堆叠上下文。这个小的堆叠上下文像父级一样有多个平面。在处理这些上下文的时候,我们可以按照从小到大的顺序递归:先梳理出最小堆叠上下文中元素的顺序,拍一张照片——作为一个整体,然后与父级堆叠上下文中的其他元素进行比较.不知道大家有没有见过小吃街上的甲骨片:把面糊放在一个大平底锅里,在面糊上放上一只小虾米,最后盖上盖子夹紧,变成一个扁扁的小块,可以清楚的看到上面虾的模样。(截图来自网络)用来压片的锅是级联上下文,面糊和虾是不同层次的HTML元素。它们在被压平之前以明确的从上到下的顺序排列,但最终都变成了一片薄薄的小饼干。这块小饼干可以放到更大的锅里,作为原料和其他材料一起继续做大饼干,但在大锅的眼里,这小饼干到底是面糊还是虾仁生的?放在上面,根本无所谓,因为现在是一个整体,只有讨论这块小饼干和其他食材放在第二个锅里的顺序才有意义。请记住,在这个模型中,层次判断非常简单。当我们面对一些难以判断的层次关系时,我们可以梳理出一个“级联上下文树”,这有点类似于dom树的结构。从小到大,将不同平面的元素堆叠在一个堆叠上下文中,压平,放入父级的堆叠上下文树中。下面会用几个例子来加深理解,都有在线链接,可以点进去看看能不能解释一下最终的渲染结果:demo1公众号后台回复107获取在线代码地址DOM的层级关系如下container1:absolute1#:absolutez-index:12#:absolutez-index:2container2:absolute3#:absolutez-index:3虽然container1和container2都是绝对定位,但是没有设置z-index,不形成堆叠上下文。所以只有根元素形成这个。元素1、2、3当然也构成了级联上下文,但是它们没有子元素,就不展开讨论了。下面的例子形成的级联上下文树也如下:根级联上下文No.1:absolutez-index:No.12:absolutez-index:No.23:absolutez-index:3最后的级联关系为不难判断,从下往上是No.1-No.。2号3添加修改,给container1和container2添加z-index,现在DOM的层级关系变成:container1:absolutez-index:No.41:absolutez-index:No.12:absolutez-index:2container2:absolutez-index:No.13:absolutez-index:3container1和container2都设置了z-index,加上根元素形成的堆叠上下文,一共三个。形成的级联上下文树如下:Rootcascadingcontextcontainer1:absolutez-index:No.41:absolutez-index:No.12:absolutez-index:2container2:absolutez-index:No.13:absolutez-index:3首先看container1形成的stackingcontext,不管它自己的z-index是多少,构成stackingcontext的元素都是在当前context的底部,后面依次是元素1和2。然后就是container2形成的级联上下文,只有一个3号元素,没什么好说的。最后,还有根堆栈上下文。在它眼里,container1和container2是一个整体,container2在底部,container1在顶部。所以最后从下往上的顺序是:container2(No.3)-container1(No.1-No.2)demo3公众号后台回复109获取在线代码地址之前的container目测不可见,现在给一种颜色。container2的z-index设置为0,现在DOM的层级关系变成:container1:absolute1#:absolutez-index:2container2:absolutez-index:02#:absolutez-index:-1现在可以了从下往上看分别是container1-container2-No.2-No.1。这个例子很难理解。让我们用堆叠上下文树来分析它。有两个堆栈上下文,一个由根元素组成,一个是container2的根堆栈上下文。container1:absolute1:absolutez-index:2container2:absolutez-index:02:absolutez-index:-1在container2形成的堆叠上下文中,只有一个元素2,即使它的z-index为负数,它将放置在container2之上。正如我之前所说,构成堆叠上下文的元素始终位于当前堆叠上下文的底部。我们将它们两个压平,形成一个整体。在rootstackingcontext中,有container1、container2和number1三个元素,container1没有设置z-index,可以看成0,和container2同级。当层级相同时,根据在html中出现的顺序来判断,所以是container1-container2;1号的层级最高,所以最后一层是container1-container2-1。最后展开container2中的其他元素,得到最终的级联关系container1-container2(2号)-No.具有明确指定的z-index值(不是自动)的定位元素将产生一个堆叠上下文,其中内部元素级别都在它上面,即使它是负数。如果是没有指定z-index的定位元素(也就是auto),那么虽然不能形成层叠上下文,但是在比较层的时候和z-index:0是一样的。如果我们也把浮动元素也放进去,就可以得到一个完整的级联层级:这张图看着复杂,其实不用背,可以一个一个看:先看块级元素,大部分我们写的代码就是它,例如div,我们可以看到它们,因为块级元素在堆栈上下文的根元素之上。然后是浮动元素和文字,浮动本身就是为了实现环绕效果,所以浮动元素和文字是在同一层级的,这样才不会被遮挡。然后是定位元素。我们知道,如果不指定z-index,即当为auto时,会在浮动元素之上,在层级关系上其实相当于0;可以继续启动,z-index>0会在z-index=0上面。唯一要记住的是z-index<0,它的层级关系在块元素之下,根元素之上形成堆叠上下文。其中,当多个堆叠层次相同的元素重叠时,根据它们在html中出现的先后顺序来确定堆叠上下关系,后出现的在最上面。CSS3的新特性除了定位z-index元素外,CSS3还提供了另一种生成堆叠上下文的方法。特殊的就不一一列举了,意义不大。开发中可能会用到:灵活布局的children(父元素display:flex|inline-flex),z-index不auto时opacity非1的元素Transformnon-none元素filternon-none元素可以生成stackingcontexts和flex子元素也可以使用z-index来更精确地设置级别。其他三个设置z-index不会生效,但在比较层次关系时会被当作z-index:0对待。结论级联上下文和z-index这两个概念是密不可分的。堆叠上下文由具有z-index属性的元素形成的平面组成;具有z-index属性的元素形成子堆叠上下文。当然这里的z-index一定要有效设置。以前是指定位元素——position是absolute/relative等,现在也可以是flex的子元素。在比较复杂元素的层叠顺序时,主要需要梳理出一棵层叠上下文树,而一个元素的层叠层次只有在当前层叠上下文中才有意义。回到前两个问题,答案不难理解。具有大z-index的元素不一定位于小元素之上。因为不一定生效,通常需要是定位元素才会生效。CSS3之后,弹性元素的子元素也可以生效;z-index生效后,并不是简单的大小比较,因为这个值只有在当前级联上下文中才有意义。要实现父元素覆盖子元素,给父元素设置大的z-index是没有用的。因为这样他就成为了一个层叠上下文的根元素,无论子元素怎么设置,都会在层叠上下文的根元素之上。正确的解决方法是将子元素的z-index设置为负数,这样父元素就是块级元素,z-index<0的子元素就会在块级元素之下,并且可以达到我们想要的效果。如果您觉得这篇文章对您有帮助,请点赞阅读~这对我来说很重要您的支持是我创作最大的动力??
