深入 CSS | 青训营笔记
这是我参与「 第四届青训营 」笔记创作活动的的第 3 天
知识回顾
在了解 CSS 中,主要学习了 CSS 规则如何作为 HTML 内容设置样式。其中重点了解了各类选择器的使用、颜色的设置、字体与文本样式的设置。
接下来将深入理解 CSS 的继承、求值、布局等知识。
课堂笔记
层叠与继承
选择器的特异度
引例:哪条规则生效?
<article>
<h1 class="title">拉森火山国家公园</h1>
</article>
<style>
.title {
color: blue;
}
article h1 {
color: red;
}
</style>
这就涉及到了选择器的优先级问题。
通常用选择器的特异度(特殊的程度)来决定规则的优先级。
计算方法:数出选择器中 id 、(伪)类、标签的个数,从左往右(特殊性依次减小)排成三位数。这个三位数就是选择器的特异度。如果特异度相同顺序靠后的规则生效。
例如:
#nav .list li a:link
.hd ul.links a
第一行:1 个 id(#nav)、 2 个(伪)类(.list , a:link)、 2 个标签(li , a),特异度 122
第二行:0 个 id 、 2 个(伪)类(.hd , .links)、 2 个标签(ul , a),特异度 22
再看一个例子:
<button class="btn">普通按钮</button>
<button class="btn primary">主要按钮</button>
<style>
.btn {
display: inline-block;
padding: .36em .8em;
margin-right: .5em;
line-height: 1.5;
text-align: center;
cursor: pointer;
border-radius: .3em;
border: none;
background: #e6e6e6;
color: #333;
}
.btn.primary {
color: #fff;
background: #218de6;
}
</style>
显然 .btn.primary 的特异度比 .btn 要高。
继承
某些属性会自动继承其父元素的计算值,除非显式指定一个值
<p>This is a <em>test</em> of <strong>inherit</strong></p>
<style>
body {
font-size: 20px;
}
p {
color: blue;
}
em {
color: red;
}
</style>
在这里父元素是 <p> ,它有子元素 em 与 strong 。
我们把段落设为了蓝色,如果不指定,强调和突出显示的文字都是蓝色;上面的例子里再显式指定了 em 的值为红色,则强调部分显示为红色。
一般与文字相关的属性都可以继承,但与盒模型相关的属性不能继承(例如宽度)。
显式继承
- 使用
inherit属性值设置,可以使不可继承的元素变为可继承。
* {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
.some-widget {
box-sizing: content-box;
}
通配选择器内设置了所有容器大小都从父元素继承。
后面两种是显式指定。
初始值
-
CSS 中,每个属性都有一个初始值
background-color的初始值为transparentmargin-left的初始值为0
-
可以使用 initial 关键字显式重置为初始值
background-color: initial
CSS 求值过程
-
首先解析 DOM 树和 CSS 样式规则;
-
filtering:对应用到该页面的规则用以下条件进行筛选:选择器匹配、属性有效、符合当前 media 等;匹配规则后就会得到每个元素具有的一组可能为零或多个的声明值(Declared Values);
-
cascading:按照来源、!important 、选择器特异性、书写顺序等选择出优先级最高的一个属性值;选出的这个值作为该元素的层叠值(Cascaded Value);
-
defaulting:当层叠值为空的时候,使用继承或初始值;得到一个一定不为空的指定值(Specified Value);
-
resolving:将一些相对值或者关键字转化为绝对值,比如 em 转化为 px ,相对路径转化为绝对路径;得到浏览器再不进行实体布局的情况下所能得到的最具体的计算值(Computed Value);
-
formatting:将计算值进一步转换,比如关键字、百分比等都转为绝对值;得到进行实际布局时的使用值(Used Value);
-
constraining:将小数像素值转化为整数;最终得到渲染时实际生效的实际值。
布局
布局(Layout)是什么
-
确定内容的大小和位置的算法
-
根据元素、容器、兄弟节点和内容等信息来计算
-
布局相关技术:
- 常规流(Normal Flow):行级、块级、表格布局、FlexBox、Grid 布局
- 浮动
- 绝对定位
盒模型(box)
width
- 指定 content box 宽度
- 取值为长度、百分数、auto
- auto 由浏览器根据其它属性确定
- 百分数相对于容器的 content box 宽度
height
- 指定 content box 高度
- 取值为长度、百分数、auto
- auto 取值由内容计算得来
- 百分数相对于容器的 content box 高度
- 容器有指定的高度时,百分数才生效
padding
- 指定元素四个方向的内边距
- 百分数相对于容器宽度
10px:四个方向内边距都为 10px
10px 20px:水平方向内边距为 10px ;垂直方向内边距为 20px
10px 20px 10px 20px:从上面内边距开始顺时针方向依次为四个内边距
border
-
指定容器边框样式、粗细和颜色
- none 无边框
- solid 实线
- dashed 虚线
- dotted 点线
-
三种属性
- border-width
- border-style
- border-color
-
四个方向
- border-top
- boder-right
- border-bottom
- border-lef
例子:
border: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 2px dotted red;
border-width: 1px 2px 3px 4px;
border-style: solid;
border-color: green blue;
border-left-width: 3px;
border-top-color: #f00;
小技巧:当四条边框颜色不同时可以把边框宽度设为零,再把其中三条边设为透明就可以得到一个三角形。
margin
- 指定元素四个方向的外边距
- 取值可以是长度、百分数、auto
- 百分数相对于容器宽度
使用 margin:auto 水平居中
<div></div>
<style>
div {
width: 200px;
height: 200px;
background: coral;
margin-left: auto;
margin-right: auto;
}
</style>
将左右外边距都设置为 auto 就可以达到左右居中的效果
maigin collapse
<div class="a"></div>
<div class="b"></div>
<style>
.a {
background: lightblue;
height: 100px;
margin-bottom: 100px;
}
.b {
background: coral;
height: 100px;
margin-top: 100px;
}
</style>
上下两个盒子的下外边距都是 100px ,但最后显示的两 content 间距只有 100px
box-sizing: border-box
定义的长宽都是边框的长宽,更符合人的直觉。
看下面这个例子:
<p class="a">
This is the behavior of width and height as specified by CSS2.1. The
specified width and height (and respective min/max properties) apply to
the width and height respectively of the content box of the element.
</p>
<p class="b">
Length and percentages values for width and height (and respective min/max
properties) on this element determine the border box of the element.
</p>
<style>
html {
font-size: 24px;
}
.a {
width: 100%;
padding: 1em;
border: 3px solid #ccc;
}
.b {
box-sizing: border-box;
width: 100%;
padding: 1em;
border: 3px solid #ccc;
}
</style>
a 元素是一个 content-box ,指定的 width 是文字的宽度,实际显示边框会超出 100%
b 元素设置为了 boder-box ,指定的 width 是边框的宽度,实际显示边框就占 100%
overflow
对于我们限定的盒子高度,其内容可能溢出,可以通过 overflow 属性来设置。
- 属性值
- visible 往盒子下穿出显示
- hidden 超出盒子的部分不显示
- scroll 增加滚动条显示
行级与块级
CSS
| Block Level Box | Inline Level Box |
|---|---|
| 不和其他盒子并列摆放 | 和其他盒子一起放在一行或拆开成多行 |
| 适用于所有的盒模型属性 | 盒模型中的width、height不适用 |
HTML
| 块级元素 | 行级元素 |
|---|---|
| 生成块级盒子 | 生成行级盒子 内容分散在多个行盒 (line box) 中 |
| body, article, div, main, section, h1~6, p, ul, li... | span, em, strong, cite, code... |
| display: block | display: inline |
display 属性
- block 块级盒子
- inline 行级盒子
- inline-block 本身是行级,可以放在行盒中;可以设置宽高;作为一个整体不会被拆散成多行
- none 排版时被完全忽略
常规流(Normal Flow)
正常布局流 (normal flow) 是指在不对页面进行任何布局控制时,浏览器默认的 HTML 布局方式。
- 根元素、浮动和绝对定位的元素会脱离常规流
- 其它元素都在常规流之内(in-flow)
- 常规流中的盒子,在某种排版上下文中参与布局
行级排版上下文
- Inline Formatting Context (IFC)
- 只包含行级盒子的容器会创建一个IFC
- IFC 内的排版规则
- 盒子在一行内水平摆放
- 一行放不下时,换行显示
- text-align 决定一行内盒子的水平对齐
- vertical-align 决定一个盒子在行内的垂直对齐
- 避开浮动(float)元素*
例子:
<div>
This is a paragraph of text with long word Honorificabilitudinitatibus. Here is an image
<img src="https://assets.codepen.io/59477/cat.png" alt="cat">
And <em>Inline Block</em>
</div>
<style>
div {
width: 10em;
//overflow-wrap: break-word;
background: #411;
}
em {
display: inline-block;
width: 3em;
background: #33c;
}
</style>
块级排版上下文
- Block Formatting Context (BFC)
- 某些容器会创建一个BFC
- 根元素
- 浮动、绝对定位、inline-block
- Flex子项和Grid子项
- overflow 值不是 visible 的块盒
- display: flow-root;
- BFC 内的排版规则
- 盒子从上到下摆放
- 垂直 margin 合并
- BFC 内盒子的 margin 不会与外面的合并
- BFC 不会和浮动元素重叠
一个行盒的例子:
<span>
This is a text and
<div>block</div>
and other text.
</span>
<style>
span {
line-height: 3;
border: 2px solid red;
background: coral;
}
div {
line-height: 1.5;
background: lime;
}
</style>
Flex Box
Flex Box 是什么?
- 一种新的排版上下文
- 它可以控制子级盒子的:
- 摆放的流向 ( → ← ↑ ↓ )
- 摆放顺序
- 盒子宽度和高度
- 水平和垂直方向的对齐
- 是否允许折行
应用 display: flex 设置布局
Flexbox 是 CSS 弹性盒子布局模块(Flexible Box Layout Module)的缩写,它被专门设计出来用于创建横向或是纵向的一维页面布局。要使用 flexbox,你只需要在想要进行 flex 布局的父元素上应用 display: flex ,所有直接子元素都将会按照 flex 进行布局。我们来看一个例子。
<div class="container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>
<style>
.container {
display: flex;
border: 2px solid #966;
}
.a, .b, .c {
text-align: center;
padding: 1em;
}
.a {
background: #fcc;
}
.b {
background: #cfc;
}
.c {
background: #ccf;
}
</style>
现在,当我们把 display: flex 添加到它的父元素时,这三个元素就自动按列进行排列。这是由于它们变成了 flex 项 (flex items),按照 flex 容器(也就是它们的父元素)的一些 flex 相关的初值进行 flex 布局:它们整整齐齐排成一行,是因为父元素上 flex-direction 的初值是 row 。它们全都被拉伸至和最高的元素高度相同,是因为父元素上 align-items 属性的初值是 stretch 。这就意味着所有的子元素都会被拉伸到它们的 flex 容器的高度,在这个案例里就是所有 flex 项中最高的一项。所有项目都从容器的开始位置进行排列,排列成一行后,在尾部留下一片空白。
行文方向:
- flex-direction 属性
- row (default)
- row-reverse
- column
- column-revers
主轴与侧轴:
主轴方向的对齐:
- justify-content 属性
- flex-start (default)
- flex-end
- center
- space-between
- space-arround
- space-evenly
侧轴方向的对齐:
- align-items 属性(在容器上设置)
- flex-start
- flex-end
- center
- stretch (default)
- baseline
- align-self 属性(在子元素上设置)
- flex-start
- flex-end
- center
- stretch (default)
- baseline
设置布局顺序:
- order 属性
- 0 (default)
- 1
- 2
- ...
Flexibility
- 可以设置子项的弹性:当容器有剩余空间时,会伸展;容器空间不够时,会收缩。
- flex-grow 有剩余空间时的伸展能力
- flex-shrink 容器空间不足时收缩的能力
- flex-basis 没有伸展或收缩时的基础长度
<div class="container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>
<style>
.container {
display: flex;
}
.a, .b, .c {
width: 100px;
}
.a {
flex-grow: 2;
}
.b {
flex-grow: 1;
}
</style>
flex-grow 初始值为 0 ,故上述例子中 a 所占空间最大, b 次之, c 最小
<div class="container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>
<style>
.container {
display: flex;
}
.a, .b, .c {
width: 400px;
}
.a {
flex-shrink: 0;
}
</style>
flex-shrink 初始值为 1 ,则上述例子 将 a 的值设为 0 ,a 将获得最大的空间
Flexibility 也可以缩写为一个 flex
| flex | flex- |
|---|---|
| flex: 1 | flex-grow: 1 |
| flex: 100px | flex-basis: 100px |
| flex: 2 1 | flex-grow: 2; flex-shrink: 1 |
| flex: 1 100px | flex-grow: 1; flex-basis: 100px |
| flex: 2 0 100px | flex-grow: 2; flex-shrink: 0; flex-basis: 100px |
| flex: auto | flex: 1 1 auto |
| flex: none | flex: 0 0 auto |
Grid 布局
Flexbox 用于设计横向或纵向的布局,而 Grid 布局则被设计用于同时在两个维度上把元素按行和列排列整齐。
- 应用 display: grid 设置布局
- display: grid 使元素生成一个块级的 Grid 容器
- 使用 grid-template 相关属性将容器划分为网格
- 设置每一个子项占哪些行/列
划分网格:
grid-template-columns: 100px 100px 200px;
grid-template-rows: 100px 100px
设置为三列两行的网格;前两列宽度为 100px ,第三列宽度为 200px ;两行高度均为 100px。
grid-template-columns: 30% 30% auto;
grid-template-rows: 100px auto
设置为三列两行的网格;前两列宽度占容器的 30% ,第三列宽度自动充满;第一行高度为 100 px ,第二行自动充满。
grid-template-columns: 100px 1fr 1fr;
grid-template-rows: 100px 1fr
设置为三列两行的网格;第一列宽度为 100px ,第二列第三列各占一份剩下宽度的一份(fraction);第一行高度为 100px ,第二行占完剩下高度的一份。
网格线:
1 2 3 4
2
3
网格区域:
行开始线1 列开始线1 行结束线3 列结束线3
例子:
.a {
grid-row-start: 1;
grid-column-start: 1;
grid-row-end: 3;
grid-column-end: 3;
}
这是把 a 选中为 行开始线1 列开始线1 行结束线3 列结束线3 的网格区域
也可以写成
.a {
grid-area: 1/1/3/3;
}
这是一种简便写法
.a {
grid-area: 2/2/4/4;
}
.b {
grid-area: 1/1/3/3;
}
浮动(Float)
<section>
<img src="https:p4.ssl.qhimg.com/t017aec0e7edc961740.jpg" width="300" alt="mojave" />
<p>
莫哈韦沙漠不仅纬度较高,而且温度要稍微低些,是命名该公园的短叶丝兰————约书亚树的特殊栖息地。约书亚树以从茂密的森林到远远间隔的实例等各种形式出现。除了约书亚树森林之外,该公园的西部包括加州沙漠里发现的最有趣的地址外观。
</p>
</section>
<style>
img {
float: left;
}
</style>
上述是一个左侧浮动文本环绕的例子。
定位(Position)
- 应用 position 属性
- static 默认值,非定位元素
- relative 相对自身原本位置偏移,不脱离文档流
- absolute 绝对定位,相对非 static 祖先元素定位
- fixed 相对于视口绝对定位
position: relative
- 在常规流里面布局
- 相对于自己本应该在的位置进行偏移
- 使用 top、left、bottom、right 设置偏移长度
- 流内其他元素当它没有偏移一样布局
position: absolute
- 脱离常规流
- 相对于最近的非 static 祖先定位
- 不会对流内元素布局造成影响
position: fixed
- 固定定位与绝对定位的工作方式完全相同,只有一个主要区别:绝对定位固定元素是相对于
<html>元素或其最近的定位祖先,而固定定位固定元素则是相对于浏览器视口本身。这意味着您可以创建固定的有用的 UI 项目,如持久导航菜单。
学习建议
- 充分利用 MDN 和 M3C CSS 规范
- 保持好奇心,善用浏览器的开发者工具
- 持续学习, CSS 新特性还在不断出现