深入 CSS | 青训营笔记

128 阅读12分钟

深入 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> ,它有子元素 emstrong

我们把段落设为了蓝色,如果不指定,强调和突出显示的文字都是蓝色;上面的例子里再显式指定了 em 的值为红色,则强调部分显示为红色。

一般与文字相关的属性都可以继承,但与盒模型相关的属性不能继承(例如宽度)。

显式继承

  • 使用 inherit 属性值设置,可以使不可继承的元素变为可继承。
* {
  box-sizing: inherit;
}

html {
  box-sizing: border-box;
}

.some-widget {
  box-sizing: content-box;
}

通配选择器内设置了所有容器大小都从父元素继承。

后面两种是显式指定。

初始值

  • CSS 中,每个属性都有一个初始值

    • background-color 的初始值为 transparent
    • margin-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;

border样式演示

小技巧:当四条边框颜色不同时可以把边框宽度设为零,再把其中三条边设为透明就可以得到一个三角形。

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 增加滚动条显示

overflow属性演示

行级与块级

CSS

Block Level BoxInline Level Box
不和其他盒子并列摆放和其他盒子一起放在一行或拆开成多行
适用于所有的盒模型属性盒模型中的width、height不适用

HTML

块级元素行级元素
生成块级盒子生成行级盒子
内容分散在多个行盒 (line box) 中
body, article, div, main, section, h1~6, p, ul, li...span, em, strong, cite, code...
display: blockdisplay: 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

flexflex-
flex: 1flex-grow: 1
flex: 100pxflex-basis: 100px
flex: 2 1flex-grow: 2; flex-shrink: 1
flex: 1 100pxflex-grow: 1; flex-basis: 100px
flex: 2 0 100pxflex-grow: 2; flex-shrink: 0; flex-basis: 100px
flex: autoflex: 1 1 auto
flex: noneflex: 0 0 auto
Grid 布局

Flexbox 用于设计横向或纵向的布局,而 Grid 布局则被设计用于同时在两个维度上把元素按行和列排列整齐。

左 Flex Box 右 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 新特性还在不断出现