昨天又翻了翻《深入解析CSS》,翻到了层叠上下文这一节,写个总结:
层叠顺序:
浏览器将 HTML 解析为 DOM 的同时还创建了另一个树形结构,叫作渲染树(render tree)。 它代表了每个元素的视觉样式和位置。同时还决定浏览器绘制元素的顺序。通常情况下,元素在代码里的顺序决定了绘制的顺序,先出现在代码中的元素先绘制,后出现的后绘制: 比如这段代码
<div>one</div>
<div>two</div>
<div>three</div>
用负margin让他们重叠:
可以看到在正常情况下(没有使用定位),代码中后出现的元素后绘制的元素会出现在先绘制的元素的前面。
但是在使用定位时,这种行为会改变,浏览器会先绘制所有非定位的元素,然后绘制定位元素。所以定位元素会出现在非定位元素的前面:
如果给上面的例子中的one和two加上position:relative,就会发现他们绘制在了前面,挡住了第三个元素。但是对于使用了定位的元素,他们的层叠关系依然是遵循前述的,先出现先绘制后出现后绘制的规则:
所以two依然是挡在one前面,因为他先绘制,但是one和two都开启了定位,所以他们的绘制顺序都在未开启定位的three前面。
说到这你可能会好奇,相对定位不是不脱离文档流吗?那为什么会出现在three前面呢?
文档流和这里的绘制顺序(后面我们会说到)是两个不同的概念,文档流是指正常情况下元素在文档中的行为,比如行内元素从左往右排列,块元素独占一行。脱离文档流是指,元素的排列规则不再遵循这个规则也不再被文档流中的元素影响。而这里只是指的元素的绘制顺序导致的层叠顺序不同。
z-index和层叠上下文:
按照之前的逻辑,无论是否开启了定位,要想改变绘制顺序,只能靠改变标签在代码中的顺序。有的代码是可以的,比如我们希望固定定位的模态框出现在所有定位元素的最顶层。因为固定定位同时也会脱离文档流,所以他在代码中的位置也就无所谓了,那么可以通过把模态框放在body的末尾,这样它就能盖住所有的开启定位的元素和正常元素。
但是有的标签,顺序不能随便改变,比如相对定位,它是依赖文档流的。还有绝对定位,它依赖于开启定位的祖先元素,那么在这种情况下就可以使用z-index属性来控制他们的层叠行为,z-index大的元素会出现在 z-index小的元素上面。
z-index的关键知识点有两个:第一,z-index只能在定位元素上生效(包括相对定位),第二,给一个定位元素加上z-index可以创建层叠上下文
一个层叠上下文包含一个元素或浏览器绘制的一组元素,给定位元素加上z-index会让它变成层叠上下文的根。根的所有后代元素都是这个层叠上下文的一部分。这就导致,不同的层叠上下文的鸿沟是无法被跨越的,层叠上下文的后代元素即使再开启一个层叠上下文也没办法出现在根元素之外,举个例子:
<div class="zindex1 positioned">
one
<div class="zindex100 positioned">nested</div>
</div>
<div class="zindex2 positioned">two</div>
<div>three</div>
<style>
.zindex1 {
z-index: 1
}
.zindex2 {
z-index: 2
}
.zindex100 {
z-index: 100;
}
.positioned {
position: absolute;
}
</style>
one和two都开启了绝对定位,并且z-index分别为1和2,one中有一个嵌套的nested,也开启了定位,z-index为100。但是它依然在父元素之内,所以它不会出现在two前面:
最后再总结一下层叠上下文内的元素的层叠顺序: 1.层叠上下文的根,它永远绘制再最底层 2.z-index为负的定位元素(以及它的子元素,还是按照前面的鸿沟不可跨越原则,它的子元素不会叠在根元素的前面的层叠上下文上) 3.非定位元素 4.z-index为正的元素(以及子元素)
还有就是,相对定位不脱离文档流,但是绝对定位和固定定位脱离文档流,说明他们二者的行为不会被文档流影响,但是这和它们的绘制顺序没有关系,只要开启了定位,绘制顺序就一定优先于普通元素。再有就是只有应用了z-index才会创建新的层叠上下文。一定要区分开这几个概念。