CSS 元素嵌套问题

date
Sep 5, 2018
slug
css-element-nested
status
Published
tags
HTML&CSS
summary
css元素嵌套引出的层叠上下文
type
Post
回顾了一下之前的代码,发现一个css中的坑即层叠顺序。什么叫层叠顺序?简单来说就是在屏幕上某一个位置堆积了很多 div 这些块哪个在上哪个在下的关系就是层叠顺序。(下面代码演示了三层堆叠)
<div class="test2">
  <div class="test3">
    <div class="test4"></div>
  </div>
</div>
.test2{
		position: absolute;
    width: 300px;
    height: 260px;
    top: 50%;
    left: 50%;
    margin-left: -150px;
    margin-top: -130px;
    background-color: blue;
}
.test3{
    position: absolute;
    width: 200px;
    height: 200px;
    top: 50%;
    left: 50%;
    margin-left: -100px;
    margin-top: -100px;
    background-color: black;
}
.test4{
    position: absolute;
    width: 150px;
    height: 150px;
    top: 50%;
    left: 50%;
    margin-left: -75px;
    margin-top: -75px;
    background-color: gray;
}
对应如下图:
notion image
如图所示,都是水平垂直居中的。这里我们主要关注层叠顺序。此时三者均为绝对定位,由test2开始从下到上依次是 test2 -> test3 -> test4 这样的顺序。test4位于最上方,此时想让test3与test4位置颠倒且test2位置不变(由下到上的顺序为test2 -> test4 -> test3),应该怎么做?肯定首先想到使用 z-index:
.test3 {
  //...
  z-index: 3
}

.test4 {
  //..
  z-index: 2
}
这样修改结果并不会改变。为什么?既然 z-index 不生效那我们肯定先去查 z-index 。
对于一个已经定位的元素(即position属性值是非static的元素),z-index 属性指定:元素在当前堆叠上下文中的堆叠层级。元素是否创建一个新的本地层叠上下文。
详情可见:
根据mdn的介绍来看,我们设定了堆叠层级,这样来说是应该可以的。但是结果却不尽人意。留意一下层叠上下文,是否跟创建的层叠上下文有关呢? 常见的创建层叠上下文(stacking context)的方式有:
  • z-index 值不为 "auto"的 绝对/相对定位
  • 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex
可能你并没有意识,但你确实会常常这样为html标签创建样式并为它创建了层叠上下文。
 
层叠上下文的特点:(mdn)
  1. 给一个 HTML 元素定位和 z-index 赋值创建一个层叠上下文,(opacity 值不为 1 的也是相同)。
  1. 层叠上下文可以包含在其他层叠上下文中,并且一起创建一个有层级的层叠上下文。
  1. 每个层叠上下文完全独立于它的兄弟元素:当处理层叠时只考虑子元素。
  1. 每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会 在父层叠上下文中 按顺序进行层叠。
具体的层叠关系如下图:
notion image
详情可见:
对于当前代码来说,我创建了三个层叠上下文元素并且一一嵌套。当我们设置test3的z-index时,test4是test3的子元素(层叠上下文特点3),会影响到test4。此时test3与test4一同来到了test2上方(其实也就是保持了原位置,因为test2没有设置zindex所以没有层叠上下文)。 这个时候再设置test4的z-index,相当于俩个层叠上下文元素在比较z-index,那按照上图所示的比较规则,test3的z-index只要大于test4就能在上面了,那为什么还是不变呢?我们来看看张老师的解释:
  1. 层叠上下文的层叠水平要比普通元素高(原因后面会说明);
  1. 层叠上下文可以阻断元素的混合模式;
  1. 层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文。
  1. 每个层叠上下文和兄弟元素独立,也就是当进行层叠变化或渲染的时候,只需要考虑后代元素。
  1. 每个层叠上下文是自成体系的,当元素发生层叠的时候,整个元素被认为是在父层叠上下文的层叠顺序中。
根据上面第3点我们知道了内部层叠受制于外部层叠,结合代码也就是说只要test3设置了z-index,那它就会带着test4一起走。当test4设置z-index时,首先要判断设置的值和test3的z-index正负是否相同,如果都为正(同为负),无论大小test3与test4的位置永不会变。既然设置俩个正值作为z-index行不通,那我们试试一正一负行得通吗?
首先设置test4的z-index为-1,此时只有一个层叠上下文test4,并且层叠顺序为:由test4开始从下到上依次是 test4 -> test2 -> test3 这样的顺序。test3位于最上方,此时如果给test3的z-index赋正值会导致test4重回原位置(test3上方),赋负值会导致test3来到了test2之下,不符合要求。既然父级元素没有操作空间了,试试父父级元素test2,根据特点3我们猜想如果test2的z-index设置正值那么会把test3(包括test4)这个整体共同带到test2之上的位置。如下:
notion image
在改变test4的z-index之后(为了看得更清晰我手动改变了长宽)我们发现达到了效果。设置父父级(test2)的z-index为1之后拥有了层叠上下文,test4设置z-index为-1也拥有了层叠上下文。此时test3作为普通的绝对定位元素保留在原位置(为了方便理解所以这样描述,这里test3其实是bfc发挥了作用),因为test2是父父级符合特点3,所以将位于底下的test4带了上来,但是test4属于test3的子元素,上来后就去和test3比较,因为test3不拥有层叠上下文保留在原位置,而此时test4的z-index为负值,所以在test3之下,test2之上显示
这里说了三个块级元素,大家还可以试试嵌套行内元素是什么效果。需要注意的是伪元素创建层叠上下文的表现是否和上面一样呢?
.test{
  position: absolute;
  width: 300px;
  height: 260px;
  top: 50%;
  left: 50%;
  margin-left: -150px;
  margin-top: -130px;
  background-color: blue;
  z-index: 1;
}
.test1 {
  position: absolute;
  width: 200px;
  height: 200px;
  top: 50%;
  left: 50%;
  margin-left: -100px;
  margin-top: -100px;
  background-color: black;
}
.test1:after{
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background-color: gray;
  opacity: 0;
  z-index: -1;
}
    
html如下:
<div class="test">
  <div class="test1"></div>
</div>
这个其实就是我遇到的问题,用伪元素创建了层叠上下文,我想让伪元素动画出现在test1的下方test的上方。和上面一样分别设置z-index为-1和1即可达到效果。

参考资料

 
层叠上下文 - CSS(层叠样式表) | MDN
我们假定用户正面向(浏览器)视窗或网页,而 HTML 元素沿着其相对于用户的一条虚构的 z 轴排开, 层叠上下文 就是对这些 HTML 元素的一个三维构想。众 HTML 元素基于其元素属性按照优先级顺序占据这个空间。 在本篇之前的部分--的渲染顺序是由其 运用 z-index,(我们认识到)某些元素 z-index 的值影响的。这是因为这些元素具有能够使他们形成一个 层叠上下文的特殊属性 。 文档中的层叠上下文由满足以下任意一个条件的元素形成: 在层叠上下文中,子元素同样也按照上面解释的规则进行层叠。 重要的是,其子级层叠上下文的 z-index 值只在父级中才有意义。子级层叠上下文被自动视为父级层叠上下文的一个独立单元。 总结: 层叠上下文可以包含在其他层叠上下文中,并且一起创建一个层叠上下文的层级。 每个层叠上下文都完全独立于它的兄弟元素:当处理层叠时只考虑子元素。 每个层叠上下文都是自包含的:当一个元素的内容发生层叠后,该元素将被作为整体在父级层叠上下文中按顺序进行层叠。 Note: 层叠上下文的层级是 HTML 元素层级的一个子级,因为只有某些元素才会创建层叠上下文。可以这样说,没有创建自己的层叠上下文的元素会被父层叠上下文 同化 。 在这个例子中,每个被定位的元素都创建了独自的层叠上下文,因为他们被指定了定位属性和 z-index 值。我们把层叠上下文的层级列在下面: 请一定要注意 DIV #4,DIV #5 和 DIV #6 是 DIV #3 的子元素,所以它们的层叠完全在 DIV #3 中被处理。一旦
层叠上下文 - CSS(层叠样式表) | MDN
Stacking context example 3 - CSS(层叠样式表) | MDN
最后一个例子展示了,在多层级的HTML结构中混合了多个定位元素且使用类选择器设置z-index属性时出现的问题。 我们来看一个用多个定位的div实现的三级菜单的例子,二级菜单和三级菜单在鼠标悬停或点击其父元素时才出现,通常这样的菜单在客户端和服务端都是由脚本生成的,所以样式规则不是通过ID选择器设置而是通过类选择器设置。 如果这个三级菜单有部分区域重叠,管理层叠顺序就会成为一个问题。 一级菜单仅仅是相对定位,所以没有创建层叠上下文。 二级菜单相对其父元素(一级菜单)绝对定位,要使二级菜单在所有一级菜单的上方,则需要使用z-index。此时每个二级菜单都创建了一个层叠上下文,而三级菜单也处于其父元素(二级菜单)创建的上下文中。 这样一来,在HTML结构中处于三级菜单后面的二级菜单,则会显示在三级菜单的上方,因为所有的二级菜单都使用了同样的z-index值,所以处于同一个层叠上下文中。 为了能更好地理解这种情况,这里列出了层叠上下文的层次结构: 可以通过移除不同级别的菜单之间的重叠,或者使用ID选择器指定独立的(不同的)z-index值,或者减少HTML的层级来解决这个问题。 Note: 在源码中你会看到三级菜单和二级菜单是由一个绝对定位元素包含很多div来实现的,这种方式在需要同时定位一组元素时很有用。 Note: the reason the sample image looks wrong - with the second level 2 overlapping the level 3 menus - is because level 2 has opacity, which creates a new stacking context. Basically, this whole sample page is incorrect and misleading.
Stacking context example 3 - CSS(层叠样式表) | MDN

© i7eo 2017 - 2022