层叠顺序(stacking level)与层叠上下文(stacking context)知多少?
z-index看上去其实很简单,根据z-index的大小决定层叠的优先级,实则深入进去,会发现内有乾坤。
看看下面这题,定一个两个divA和B,被包括在同一个父div标签下。HMTL结构如下:
<div class="container">
<div class="inline-block">#divA display:inline-block</div>
<div class="float">#divB float:left</div>
</div>
它们的css定义如下:
.container{
position:relative;
background:#ddd;
}
.container > div{
width:200px;
height:200px;
}
.float{
float:left;
background-color:#deeppink;
}
.inline-block{
display:inline-block;
background-color:yellowgreen;
margin-left:-100px;
}
大概描述起来,意思就是拥有共同父级容器的两个DIV重叠在一起,是display:inline-block在上面,还是float:left在上面?
注意这里DOM的顺序,是先生成display:inline-block,再生成float:left。当然也可以把两个DOM顺序调转如下:
<div class="container">
<div class="float">#divB float:left</div>
<div class="inline-block">#divA display:inline-block</div>
</div>
会发现,无论顺序如何,始终是display:inline-block的div在上面。
原理分析:
这里其实涉及了所谓的层叠顺序(stacking level),有一张图可以很好的诠释:
运用上图的逻辑,上面的题目就迎刃而解了,inline-block的stacking level比float要高,所以无论DOM的先后顺序都是堆叠在上面。
不过上面图示的说法有一些不准确,按照W3C官方的说法,准确的7层为:
- 形成堆叠上下文环境的元素的背景与边框
- 拥有负
z-index的子堆叠上下文元素(负的越高堆叠层级越低) - 正常流式布局,非
inline-block,无position定位(static除外)的子元素 - 无
position定位(static除外)的float浮动元素 - 正常流式布局,
inline-block元素,无position定位(static除外)的子元素(包括display:table和display:inline) - 拥有
z-index:0的子堆叠上下文元素 - 拥有正
z-index的子堆叠上下文元素(正的越高堆叠层级越高)
所以我们的两个div的比较是基于上面所列出来的4和5。5的stacking level更高,所以叠得更高。
不过!不过!不过!重点来了,请注意,上面的比较是基于两个div都没有形成堆叠上下文这个为基础的。下面我们修改一下题目,给两个div,增加一个opacity
.container{
position:relative;
background:#ddd;
}
.container > div{
width:200px;
height:200px;
opacity:0.9; //注意这里,增加一个opacity
}
.float{
float:left;
background-color:#deeppink;
}
.inline-block{
display:inline-block;
background-color:yellowgreen;
margin-left:-100px;
}
会看到,inline-block的div不再一定叠在float的div之上,而是和HTML代码中DOM的堆放顺序有关,后添加的div会叠在先添加的div之上。
这里的关键点在于,添加的opacity:0.9这个让两个div都生成了stacking context(堆叠上下文)的概念。此时,要对两者进行层叠排列,就需要z-index,z-index越高的层叠层级越高。
堆叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。
那么,如何触发一个元素形成堆叠上下文?方法如下:摘自MDN:
- 根元素(HTML)
- z-index值不为“auto”的绝对/相对定位
- 一个z-index值不为“auto”的flex项目(flex item),即父元素display:flex|inline-flex
- opacity属性值小于1的元素(参考the specification for opacity)
- transform属性值不为“none”的元素
- mix-blend-mode属性值不为“normal”的元素
- filter值不为“none”的元素
- perspective值不为“none”的元素
- isolation属性被设置为“isolate”的元素
- position:fixed
- 在will-change中指定了任意CSS属性,即便你没有直接指定这些属性的值
- -webkit-overflow-scrolling属性被设置“touch”的元素
所以,上面我们给两个div添加opacity属性的目的就是为了形成stacking context。也就是说添加opacity替换成上面列出来这些属性都是可以达到同样的效果。
在层叠上下文中,其子元素同样也按照上面解释的规则进行层叠。特别值得一提的是,其子元素的z-index值只在父级层叠上下文中有意义。意思就是父元素的z-index低于父元素另一个同级元素,子元素z-index再高也没用。
不受控制的position:fixed
大家都知道,position:fixed在日常的页面布局中非常有用,在许多布局中起到了关键作用。它的作用是:position:fixed的元素将相对于屏幕视口(viewport)的位置来指定其位置。并且元素的位置在屏幕滚动时不会改变。但是,在许多特定的场合,制定了position:fixed的元素却无法相对于屏幕视口进行定位,这是为何呢?
失效的position:fixed
在许多情况下,position:fixed将会失效。MDN用一句话概括了这种情况:
当元素祖先的transform属性非none时,定位容器由视口改为该祖先。
what!还有这种操作?可能部分同学还没get到上面这句话的意思,通俗的讲就是指定了position:fixed的元素,如果其祖先元素存在非none的transform值,那么该元素将相对于设定了transform的祖先元素进行定位。
这个问题,就牵涉到了上面提到的Stacking Context,解释上面失效的原因分为两步:
- 任何非none的transform值都会导致一个层叠上下文(Stacking Context)和包含块(Containing Block)的创建
- 由于层叠上下文的创建,该元素会影响其子元素的固定定位。设置了
position:fixed的子元素将不会基于viewport定位,而是基于这个父元素
一探position:fixed失效的最终原因
不是所有能够生成层叠上下文的元素都会使得position:fixed失效,但也不止transform会使position:fixed失效。
所以,MDN关于position:fixed的补充描述不够完善。下述3种方式目前都会使得position:fixed定位的基准元素改变:
transform属性值不为none的元素perspective值不为none的元素- 在
will-change中指定了任意CSS属性