CSS 世界学习笔记 - 层叠规则
概述
这本书讲解的是 CSS2.1 的世界,深入讲解 CSS 各种基础概念以及 CSS 属性、并通过示例代码来进行证明。里面还有一些作者自己总结的理论,当然他是通过实践来证明是这样的,如果你有疑问还可以到他的博客中提出来。
作者把 CSS 世界比作一个魔法世界,然后通过这种方式来讲解 CSS 知识,看起来也是比较有趣的。
作者是使用主流浏览器 Chrome、Firefox、 IE 8(反正已经被抛弃了,可以忽略)来进行测试,来说明各个浏览器厂商对 CSS 标准的实现。
作者通过实践来说明一些 CSS 的特性时,都有对应的代码实例,并且放到了他的博客上,这点我觉得非常好。
书里深入介绍了很多 CSS 的属性,通过深入了解它们的特性,你会对平时开发遇到的一些奇怪问题有所感悟,因为这都是各种 CSS 属性的特性融合在一起造成的。
书里大的章节主要是以下面几个方面展开的,很多基础概念,非常值得我们去深入学习一下。
- 流、元素与基本尺寸
- 盒尺寸
- 内联元素与流
- 流的破坏与保护
- CSS 世界层叠规则
- 强大的文本处理能力
- 元素的装饰与美化
- 元素的显示与隐藏
- 。。。
CSS 世界的层叠规则
说到层叠,对于我来说,比较熟悉的就是 z-index
属性了,平时都是跟定位元素(也就是 position
不为 static
) 进行使用,值可以设置为正值与负值,理论上是数值越大,层级越高,但是实际上层叠规则是更加复杂的。
z-index
只是层叠规则中一个小的部分,用作者的话说就是层叠规则中的一片小舟。
概念
层叠上下文
层叠上下文(stacking context)是 HTML
中的一个三维概念,如果一个元素含有层叠上下文,那么它在 z 轴上就是“高人一等”。
z 轴就是我们与显示器之间的一条看不见的水平线,大概就是下面这个图这样:
层叠水平
层叠水平(stacking level)决定了元素在同一层叠上下文中显示的顺序。
需要注意的是 z-index
不直接等于层叠水平,但是 z-index
在某些情况下会影响到元素的层叠水平,例如定位元素(也就是 position
不为 static
)。
元素的层叠顺序
层叠顺序(stacking order)可以看做是元素在垂直水平上发生层叠时的一个显示规则。
下面的图代表着 CSS2.1 世界的层叠顺序规则(注意是不包括 CSS3)
每一个层叠顺序仅适用于当前层叠上下文元素的小世界。
内联元素的层叠水平比块级元素更高,那是因为 CSS 本来就是为了更好地展示图文而设计的,所以层叠显示水平理应更高。
示例:
<style>
div {
width: 256px;
float: left;
}
span {
margin-left: -256px;
color: white;
width: 256px;
display: inline-block;
}
</style>
<span>
我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女我是美女
</span>
<div>
<img src="https://demo.cssworld.cn/images/common/l/1.jpg" width="256" />
</div>
层叠的黄金准则
当元素发生层叠时,基本上就是以下面两种情况为准:
- 当元素具有明显的层叠标识时,例如
z-index
,那么谁大谁就在上面显示 - 当发生层叠的元素具有相同水平层叠顺序时,那么在 DOM 流中,后者居上
深入理解层叠上下文
层叠上下文的特性
- 层叠上下文的层叠水平比普通元素高
- 层叠上下文可以阻断元素的混合模式(理解CSS3 isolation: isolate的表现和作用)
- 层叠上下文可以嵌套,内部层叠上下文及其所有子元素均受制于外部的层叠上下文
- 每个层叠上下文和兄弟独立,也就是说,当进行层叠变化或渲染的时候,只需要考虑后代元素。
- 每个层叠上下文是自成体系的,当元素发生层叠时,整个元素被认为是在父层叠上下文的层叠顺序中
在后面的一些代码示例中,就可以了解到层叠上下文的特性。
层叠上下文的创建
作者在书中总结出3个流派:
- 天生派:页面根元素天生具有折叠上下文
- 正统派:指的是那些
z-index
的值为数值的定位元素 - 扩招派:其它CSS3 属性
天生派 - 根折叠上下文
可以看做是 html
元素,它就是根折叠上下文。
正统派 - 定位元素与传统上下文
当元素的 postion
为 relative
或 absolute
时,只要 z-index
的值不为 auto
,则就会创建层叠上下文。position: fixed
的元素天然具有层叠上下文。
示例1:
《CSS世界》的示例代码
<div style="position:relative; z-index:auto;">
<!-- 美女 -->
<img src="1.jpg" style="position:absolute; z-index:2;">
</div>
<div style="position:relative; z-index:auto;">
<!-- 美景 -->
<img src="2.jpg" style="position:relative; z-index:1;">
</div>
首先图片的容器因为 z-index:auto
,所以没有创建层叠上下文,只是一个普通元素,而容器中的图片都为 z-index
设置了值,所以成为了层叠上下文,那么就会根据层叠的黄金准则,谁的值大,谁就在上面显示,所以美女图显示在了美景图上面。
示例2:
《CSS世界》的示例代码
<div style="position:relative; z-index:0;">
<!-- 美女 -->
<img src="1.jpg" style="position:absolute; z-index:2;">
</div>
<div style="position:relative; z-index:0;">
<!-- 美景 -->
<img src="2.jpg" style="position:relative; z-index:1;">
</div>
当图片容器设置了 z-index:0
,则该元素就创建了折叠上下文,那么折叠规则就发生了变化。因为容器变成了折叠上下文,自成一个体系,所以里面的子元素就被限制在这个容器中了,折叠的顺序会优先对比外面两个容器,因为它们的层级是一样的,所以根据层叠的黄金准则,后来居上,那么美景图就会在美女图上面。
里面图片设置的 z-index
相当于没有起效果。
position: fixed
现在应该是属于天生派了,生来就具有折叠上下文,我在 Chrome
和 Firefox
都测试了下:
<div style="position: fixed; z-index: auto">
<!-- 美女 -->
<img
src="https://demo.cssworld.cn/images/common/l/1.jpg"
style="position: absolute; z-index: 2"
/>
</div>
<div style="position: fixed; z-index: auto">
<!-- 美景 -->
<img
src="https://demo.cssworld.cn/images/common/l/2.jpg"
style="position: relative; z-index: 1"
/>
</div>
就算我给容器设置成 z-index: auto
,但美景图还是在美女图上方展示。
CSS3 与新时代的折叠上下文
CSS3 中有不少属性会影响到折叠的规则,使之创建折叠上下文:
- 元素为
flex
布局(父元素display: flex | inline-flex
),并且z-index
不为auto
- 元素的
opacity
不为 1 - 元素的
transform
不为none
- 元素的
filter
不为none
- 元素的
mix-blend-mode
不为normal
- 元素的
isolation
值为isolate
- 元素的
will-change
属性值为上面 2 ~ 6 的任意一个(如will-change: transform
、will-change: opacity
等)** 这是一个实验中的功能 ** - 元素的
-webkit-overflow-scrolling
为touch
层叠上下文与层叠顺序
普通元素一旦有了折叠上下文,那么折叠顺序就会变高,但是这个级别到底是有多高,可以根据下面两种情况来看。
- 如果层叠上下文元素不依赖于
z-index
,那么层叠上下文元素就相当于拥有z-index: auto | 0
- 如果层叠上下文依赖于
z-index
,那么就以z-index
的值大小来决定层叠顺序
根据上面的元素折叠顺序图,那么层叠上下文的层叠顺序如下:
示例1:
<div>
<!-- 美景 -->
<img
src="https://demo.cssworld.cn/images/common/l/2.jpg"
width="256"
style="position: absolute; top: 0; left: 50px"
/>
<!-- 美女 -->
<img src="https://demo.cssworld.cn/images/common/l/1.jpg" width="256" />
</div>
通过这个示例就可以就可以看出来了,本来大家都是普通元素,但是美景图因为设置了绝对定位,让 z-index
生效了,也就是 z-index: auto
,成为了层叠上下文元素,所以层叠的顺序就在普通元素之上了。
示例2:
《CSS世界》代码示例
下面这段代码主要展示内容就是盒子中有张图片,然后文字通过绝对定位在盒子底部进行展示,这时文字是在图片之上的。 图片有个动画,就是淡出的效果
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
.box {
width: 256px; height: 192px;
position: relative;
}
.text {
line-height: 30px;
position: absolute; left: 0; right: 0; bottom: 0;
background-color: rgba(0,0,0,.5);
color: #fff;
text-align: center;
font-size: 14px;
}
.fade {
animation: fadeIn 2s 2s infinite;
}
<div class="box">
<span class="text">只有图片淡出,文案一直100%透明</span>
<img class="fade" src="1.jpg">
</div>
如果你通过链接去查看了演示代码,发现在淡出的时候,文字居然变透明了:
是不是很神奇,这是因为图片执行动画时被设置了 opacity
属性,并且值不为 1 时,图片元素创建了层叠上下 文,所以它的层叠等级就跟 span
元素一样,那么这时就根据 DOM
流中的顺序来进行展示,所以图片盖住了文字,还能看到文字是因为图片透过去的,所以就成了这么个效果。
如果要一直保持文字在最前端展示,那么有两种解决方式:
- 调整元素在
DOM
流中的顺序 - 将文字的
z-index
设置为 1
z-index 负值
z-index 负值的层叠顺序
在元素层叠顺序图中,z-index
负值的层叠级别如下:
通过这个这图可以看到,哪怕你设置 z-index: -9999999
也是突破不了层叠上下文的,因为层叠上下文自成一个体系,形成了一个“结界”的小世界。
示例1:
<style>
.box {
background-color: blue;
}
.box > img {
position: relative;
z-index: -1;
right: -50px;
}
</style>
<div class="box">
<img src="1.jpg">
</div>
在上面代码中,图片设置了 position: relative
后,成为了层叠上下文元素,因为 z-index
是负值,所以按照层叠顺序图来说,那么它就理所当然地跑到块级盒子(蓝色背景)的后面去了。
示例2:
<style>
.box {
background-color: blue;
}
.box > img {
position: relative;
z-index: -1;
right: -50px;
}
.context {
transform: scale(1);
}
</style>
<div class="box context">
<img src="1.jpg">
</div>
一个使用示例
《CSS世界》示例代码
<style>
.container {
background-color: #666;
/* 创建层叠上下文 */
position: relative;
z-index: 0;
}
.page {
width: 600px;
background-color: #f4f39e;
background: linear-gradient(to bottom, #f4f39e, #f5da41 60%, #fe6);
box-shadow: 0 2px 10px 1px rgba(0, 0, 0, .2);
text-shadow: 0 1px 0 #f6ef97;
position: relative;
}
.page:before {
transform: skew(-15deg) rotate(-5deg);
transform-origin: left bottom;
left: 0;
}
.page:after {
transform: skew(15deg) rotate(5deg);
transform-origin: right bottom;
right: 0;
}
/* 边角卷边阴影 */
.page:before, .page:after {
width: 90%; height: 20%;
content: "";
box-shadow: 0 8px 16px rgba(0, 0, 0, .3);
position: absolute;
bottom: 0;
z-index: -1;
}
</style>
<div class="container">
<div class="page">
<h4 class="title">一些示例文字</h4>
<p>有个老头儿的狗死了,...</p>
<p>本来抱着大哭一场...</p>
<p>今天全家看...</p>
<p>病童:"打针前为什么..."</p>
</div>
</div>
效果图如下,边角卷边阴影在黄色纸张之上,灰色背景之下,就形成了对应的阴影效果:
把灰色背景隐藏后,阴影的效果会明显些:
首先,最外层的 .container
灰色背景盒子通过 position: relative
创建了层叠上下文,属性 z-index: 0
。
.page
盒子通过 position: relative
创建了层叠上下文,属性 zindex
默认为 auto
。
边角卷边阴影通过 position: absolute
创建了层叠上下文,属性 z-index: -1
。
通过下面的层叠顺序图,就可以知道它们的层叠顺序了:
z-index “不犯二” 准则
作者以他多年的实践经验总结出了一个“不犯二”准则,意思是指非浮层元素(各种顶层弹窗、消息等)的 z-index
的值不应该超过 2。
原因如下:
- 定位元素一旦设置了
z-index
值,就从普通元素变成了层叠上下文,那么级别就变高了,在某些情况会出现z-index
设置得再大也无法覆盖个别元素的情况 - 如果随意得设置
z-index
,很容易出现样式混乱问题,例如在一个地方使用一个小图标,随意设置了个值,后来其他人发现自己的实现被覆盖了,就随意设置了一个更大的值,那么情况会变得越来越复杂,说不定哪天就把某个顶层弹窗也给覆盖了
对于使用 JavaScript
来驱动的浮层组件,作者建议使用“层级计数器”来解决,也就是遍历在 <body>
下的子元素,找出 z-index
最大值,然后进行 + 1 来进行使用。使用这种方法对于那些意想不到的高层元素会很有用,因为组件的覆盖规则具有动态性。
上一篇: 谦虚的小K