重学前端2 - CSS

CSS


09 | CSS语法:除了属性和选择器,你还需要知道这些带@的规则

CSS 的顶层样式表由两种规则组成的规则列表构成,一种被称为 at-rule,也就是 at 规则,另一种是 qualified rule,也就是普通规则

at-rule 由一个 @ 关键字和后续的一个区块组成,如果没有区块,则以分号结束。这些 at-rule 在开发中使用机会远远小于普通的规则,所以它的大部分内容,你可能会感觉很陌生。

这些 at 规则正是掌握 CSS 的一些高级特性所必须的内容。qualified rule 则是指普通的 CSS 规则,也就是我们所熟识的,由选择器和属性指定构成的规则。

at 规则

  • @charset : https://www.w3.org/TR/css-syntax-3/
  • @import :https://www.w3.org/TR/css-cascade-4/
  • @media :https://www.w3.org/TR/css3-conditional/
  • @page : https://www.w3.org/TR/css-page-3/
  • @counter-style :https://www.w3.org/TR/css-counter-styles-3
  • @keyframes :https://www.w3.org/TR/css-animations-1/
  • @fontface :https://www.w3.org/TR/css-fonts-3/
  • @supports :https://www.w3.org/TR/css3-conditional/
  • @namespace :https://www.w3.org/TR/css-namespaces-3/

普通规则

qualified rule 主要是由选择器和声明区块构成。声明区块又由属性和值构成。我在下面的列表中,介绍了这部分语法的组成要点。

  • 普通规则
    • 选择器
    • 声明列表
      • 属性
        • 值的类型
        • 函数

选择器

我们从语法结构可以看出,任何选择器,都是由几个符号结构连接的:空格、大于号、加号、波浪线、双竖线,这里需要注意一下,空格,即为后代选择器的优先级较低。

然后对每一个选择器来说,如果它不是伪元素的话,由几个可选的部分组成,标签类型选择器,id、class、属性和伪类,它们中只要出现一个,就构成了选择器。

如果它是伪元素,则在这个结构之后追加伪元素。只有伪类可以出现在伪元素之后。我在下面用一个列表(不太严谨地)整理了选择器的语法结构:

重学前端2 - CSS_第1张图片

我们在这里可以参考一个示例图:

重学前端2 - CSS_第2张图片

声明:属性和值

声明部分是一个由“属性: 值”组成的序列。

属性是由中划线、下划线、字母等组成的标识符,CSS 还支持使用反斜杠转义。我们需要注意的是:属性不允许使用连续的两个中划线开头,这样的属性会被认为是 CSS 变量

以双中划线开头的属性被当作变量,与之配合的则是 var 函数:

:root {
  --main-color: #06c;
  --accent-color: #006;
}
/* The rest of the CSS file */
#foo h1 {
  color: var(--main-color);
}

值的部分,主要在标准 CSS Values and Unit,根据每个 CSS 属性可以取到不同的值,这里的值可能是字符串、标识符。

CSS 属性值可能是以下类型。

  • CSS 范围的关键字:initial,unset,inherit,任何属性都可以的关键字。
  • 字符串:比如 content 属性。
  • URL:使用 url() 函数的 URL 值。
  • 整数 / 实数:比如 flex 属性。
  • 维度:单位的整数 / 实数,比如 width 属性。
  • 百分比:大部分维度都支持。
  • 颜色:比如 background-color 属性。
  • 图片:比如 background-image 属性。
  • 2D 位置:比如 background-position 属性。
  • 函数:来自函数的值,比如 transform 属性。

这里我们要重点介绍一下函数。一些属性会要求产生函数类型的值,比如 easing-function 会要求 cubic-bezier() 函数的值:

CSS 支持一批特定的计算型函数:

  • calc()
  • max()
  • min()
  • clamp()
  • toggle()
  • attr()

calc()函数是基本的表达式计算,它支持加减乘除四则运算。在针对维度进行计算时,calc() 函数允许不同单位混合运算,这非常的有用。

section {
  float: left;
  margin: 1em; border: solid 1px;
  width: calc(100%/3 - 2*1em - 2*1px);
}

max()、min() 和 clamp() 则是一些比较大小的函数,max() 表示取两数中较大的一个,min() 表示取两数之中较小的一个,clamp() 则是给一个值限定一个范围,超出范围外则使用范围的最大或者最小值。

toggle() 函数在规则选中多于一个元素时生效,它会在几个值之间来回切换,比如我们要让一个列表项的样式圆点和方点间隔出现,可以使用下面代码:

ul { list-style-type: toggle(circle, square); }

attr() 函数允许 CSS 接受属性值的控制。


20 | CSS 选择器:如何选中svg里的a元素?

我先来讲讲选择器是什么,选择器是由 CSS 最先引入的一个机制(但随着 document.querySelector 等 API 的加入,选择器已经不仅仅是 CSS 的一部分了)。我们今天这一课,就重点讲讲 CSS 选择器的一些机制。

选择器的基本意义是:根据一些特征,选中元素树上的一批元素

  • 简单选择器:针对某一特征判断是否选中元素。
  • 复合选择器:连续写在一起的简单选择器,针对元素自身特征选择单个元素。
  • 复杂选择器:由 “(空格)”、“ >; ”、“ ~ ”、“ + ”、“ || ” 等符号连接的复合选择器,根据父元素或者前序元素检查单个元素。
  • 选择器列表:由逗号分隔的复杂选择器,表示“或”的关系。

选择器是由简单选择器逐级组合而成的结构。

简单选择器

重学前端2 - CSS_第3张图片

类型选择器和全体选择器

根据一个元素的标签名来选中元素。

这看上去非常简单,但是实际上,我们还必须要考虑 html 或者 xml 元素的命名空间问题

id 选择器与 class 选择器

id 选择器和 class 选择器都是针对特定属性的选择器。id 选择器是“#”号后面跟随 id 名,class 选择器是“.”后面跟随 class 名。

属性选择器

属性选择器根据 HTML 元素的属性来选中元素。属性选择器有四种形态。

  • 第一种,[att]

直接在方括号中放入属性名,是检查元素是否具有这个属性,只要元素有这个属性,不论属性是什么值,都可以被选中。

  • 第二种,[att=val]

精确匹配,检查一个元素属性的值是否是 val。

  • 第三种,[att~=val]

多种匹配,检查一个元素的值是否是若干值之一,这里的 val 不是一个单一的值了,可以是用空格分隔的一个序列

  • 第四种,[att|=val]

开头匹配,检查一个元素的值是否是以 val 开头,它跟精确匹配的区别是属性只要以 val 开头即可,后面内容不管。

有些 HTML 属性含有特殊字符,这个时候,可以把 val 用引号括起来,形成一个 CSS 字符串。CSS 字符串允许使用单双引号来规避特殊字符,也可以用反斜杠转义,这样,就可以表示出任意属性值啦。

伪类选择器

伪类选择器是一系列由 CSS 规定好的选择器,它们以冒号开头。伪类选择器有普通型和函数型两种。

  • 树结构关系伪类选择器

:root 伪类表示树的根元素,在选择器是针对完整的 html 文档情况,我们一般用 html 标签即可选中根元素。但是随着 scoped css 和 shadow root 等场景出现,选择器可以针对某一子树来选择,这时候就很需要 root 伪类了。

:empty 伪类表示没有子节点的元素,这里有个例外就是子节点为空白文本节点的情况。

:nth-child 和 :nth-last-child 这是两个函数型的伪类,CSS 的 An+B 语法设计的是比较复杂的,我们这里仅仅介绍基本用法。我们还是看几个例子:

重学前端2 - CSS_第4张图片

:nth-last-child 的区别仅仅是从后往前数。

:first-child 表示第一个元素。

:last-child 表示最后一个元素。

:only-child 按字面意思理解即可,选中唯一一个子元素。

of-type 系列,是一个变形的语法糖,S:nth-of-type(An+B) 是:nth-child(|An+B| of S) 的另一种写法。

以此类推,还有 nth-last-of-type、first-of-type、last-of-type、only-of-type。

链接与行为伪类选择器

  • :any-link 表示任意的链接,包括 a、area 和 link 标签都可能匹配到这个伪类。
  • :link 表示未访问过的链接,:visited 表示已经访问过的链接。
  • :hover 表示鼠标悬停在上的元素,
  • :active 表示用户正在激活这个元素,如用户按下按钮,鼠标还未抬起时,这个按钮就处于激活状态
  • :focus 表示焦点落在这个元素之上。
  • :target 用于选中浏览器 URL 的 hash 部分所指示的元素。

在 Selector Level 4 草案中,还引入了 target-within、focus-within 等伪类,用于表示 target 或者 focus 的父容器。

逻辑伪类选择器

我们这里介绍一个逻辑伪类 —— :not 伪类。

这个伪类是个函数型伪类,它的作用时选中内部的简单选择器命中的元素。

其它伪类选择器

  • 国际化:用于处理国际化和多语言问题。

dir | lang

  • 音频 / 视频:用于区分音视频播放状态。

play | pause

  • 时序:用于配合读屏软件等时序性客户端的伪类。

current | past | future

  • 表格:用于处理 table 的列的伪类。

nth-col | nth-last-col

伪类是很大的一类简单选择器,它是选择器能力的一种补充。在实际使用中,我还是建议你尽量通过合适的 id 和 class 来标识元素,约束伪类的使用。最好只在不得不使用伪类的场景使用伪类,这对于 CSS 代码的性能和可读性都有好处。

结语

  • 类型选择器:根据一个元素的标签名来选中元素。
  • 全体选择器:与类型选择器类似,选择任意元素。
  • id 选择器:# 后面跟随 id 名。
  • class 选择器:. 后面跟随 class 名。
  • 伪类选择器:一系列由 CSS 规定好的选择器,它们以冒号开头,伪类有普通型和函数型。

21 | CSS选择器:伪元素是怎么回事儿?

选择器的组合

在 CSS 规则中,选择器部分是一个选择器列表。

选择器列表是用逗号分隔的复杂选择器序列;复杂选择器则是用空格、大于号、波浪线等符号连接的复合选择器;复合选择器则是连写的简单选择器组合。

根据选择器列表的语法,选择器的连接方式可以理解为像四则运算一样有优先级。

  • 第一优先级是无连接符号;
  • 第二优先级是:“空格”、“~”、“+”、“>”、“||”;
  • 第三优先级是:“,”。

复合选择器表示简单选择器中“且”的关系,例如,例子中的“ .b.d ”,表示选中的元素必须同时具有 b 和 d 两个 class。

复杂选择器是针对节点关系的选择,它规定了五种连接符号。

  • “空格”:后代,表示选中所有符合条件的后代节点, 例如“ .a .b ”表示选中所有具有 class 为 a 的后代节点中 class 为 b 的节点。
  • “>;” :子代,表示选中符合条件的子节点,例如“ .a>.b ”表示:选中所有“具有 class 为 a 的子节点中,class 为 b 的节点”。
  • “~” : 后继,表示选中所有符合条件的后继节点,后继节点即跟当前节点具有同一个父元素,并出现在它之后的节点,例如“ .a~.b ”表示选中所有具有 class 为 a 的后继中,class 为 b 的节点。
  • “+”:直接后继,表示选中符合条件的直接后继节点,直接后继节点即 nextSlibling。例如 “.a+.b ”表示选中所有具有 class 为 a 的下一个 class 为 b 的节点。
  • “||”:列选择器,表示选中对应列中符合条件的单元格。

我们在实际使用时,比较常用的连接方式是“空格”和“>”

工程实践中一般会采用设置合理的 class 的方式,来避免过于复杂的选择器结构,这样更有利于维护和性能。

逗号表示“或”的关系,实际上,可以把它理解为“两条内容一样的 CSS 规则”的一种简写。

选择器的优先级

CSS 标准用一个三元组 (a, b, c) 来构成一个复杂选择器的优先级。

  • id 选择器的数目记为 a;
  • 伪类选择器和 class 选择器的数目记为 b;
  • 伪元素选择器和标签选择器数目记为 c;
  • “*” 不影响优先级。

CSS 标准建议用一个足够大的进制,获取“ a-b-c ”来表示选择器优先级。

行内属性的优先级永远高于 CSS 规则,浏览器提供了一个“口子”,就是在选择器前加上“!import”。这个用法非常危险,因为它相当于一个新的优先级,而且此优先级会高于行内属性。

同一优先级的选择器遵循“后面的覆盖前面的”原则,我们可以看一个例子:

伪元素

  • ::first-line
  • ::first-letter
  • ::before
  • ::after

::first-line 和 ::first-letter 是比较类似的伪元素,其中一个表示元素的第一行,一个表示元素的第一个字母。

CSS 标准只要求 ::first-line 和 ::first-letter 实现有限的几个 CSS 属性,都是文本相关,这些属性是下面这些。

重学前端2 - CSS_第5张图片

接下来我们说说 ::before 和 ::after 伪元素。

这两个伪元素跟前面两个不同的是,它不是把已有的内容套上一个元素,而是真正的无中生有,造出一个元素。

::before 表示在元素内容之前插入一个虚拟的元素。

::after 则表示在元素内容之后插入一个虚拟的元素。

这两个伪元素所在的 CSS 规则必须指定 content 属性才会生效。

::before 和 ::after 中支持所有的 CSS 属性。实际开发中,这两个伪元素非常有用,有了这两个伪元素,一些修饰性元素,可以使用纯粹的 CSS 代码添加进去,这能够很好地保持 HTML 代码中的语义,既完成了显示效果,又不会让 DOM 中出现很多无语义的空元素。

总结

这一课,我们讲了 CSS 选择器的三种机制:选择器的组合、选择器优先级、以及伪元素。

在选择器组合这一部分,我们讲到了,选择器的连接方式像四则运算一样有优先级,

第一优先级是无连接符号;第二优先级是:“空格”、“~”、“+”、“>”、“||”;第三优先级是“,”。


24 | CSS排版:从毕昇开始,我们就开始用正常流了

正常流的行为

我们可以用一句话来描述正常流的排版行为,那就是:依次排列,排不下了换行

在正常流基础上,我们有 float 相关规则,使得一些盒占据了正常流需要的空间,我们可以把 float 理解为“文字环绕”。

重学前端2 - CSS_第6张图片

我们还有 vertical-align 相关规则规定了如何在垂直方向对齐盒。vertical-align 相关规则看起来复杂,但是实际上,基线、文字顶 / 底、行顶 / 底都是我们正常书写文字时需要用到的概念,只是我们平时不一定会总结它们。

下图展示了在不同的 vertical-align 设置时,盒与文字是如何混合排版的。为了方便你理解,我们用代码给大家标注了基线、文字顶 / 底、行顶 / 底等概念

除此之外,margin 折叠是很多人非常不理解的一种设计,但是实际上我们可以把 margin 理解为“一个元素规定了自身周围至少需要的空间”,这样,我们就非常容易理解为什么 margin 需要折叠了。

正常流的原理

在 CSS 标准中,规定了如何排布每一个文字或者盒的算法,这个算法依赖一个排版的“当前状态”,CSS 把这个当前状态称为“格式化上下文(formatting context)”。

我们可以认为排版过程是这样的:

格式化上下文 + 盒 / 文字 = 位置

我们需要排版的盒,是分为块级盒行内级盒的,所以排版需要分别为它们规定了块级格式化上下文和行内级格式化上下文。

块级格式化上下文顺次排列元素:

重学前端2 - CSS_第7张图片

行内级格式化上下文顺次排列元素:

重学前端2 - CSS_第8张图片

当我们要把正常流中的一个盒或者文字排版,需要分成三种情况处理。

  • 当遇到块级盒:排入块级格式化上下文。
  • 当遇到行内级盒或者文字:首先尝试排入行内级格式化上下文,如果排不下,那么创建一个行盒,先将行盒排版(行盒是块级,所以到第一种情况),行盒会创建一个行内级格式化上下文。
  • 遇到 float 盒:把盒的顶部跟当前行内级上下文上边缘对齐,然后根据 float 的方向把盒的对应边缘对到块级格式化上下文的边缘,之后重排当前行盒。

我们以上讲的都是一个块级格式化上下文中的排版规则,实际上,页面中的布局没有那么简单,一些元素会在其内部创建新的块级格式化上下文,这些元素有:

  • 浮动元素;
  • 绝对定位元素;
  • 非块级但仍能包含块级元素的容器(如 inline-blocks, table-cells, table-captions);
  • 块级的能包含块级元素的容器,且属性 overflow 不为 visible。

正常流的使用技巧

  • 等分布局问题
  • 自适应宽

结语

用一句话来描述正常流的排版行为,那就是:依次排列,排不下了换行

我们将正常流的知识分成了三个部分。

  • 正常流的行为部分,我们从一些感性认知出发,帮助你从思路和源头上理解正常流的行为。
  • 正常流的原理部分,我用更严格的描述方式,给你讲解了 CSS 标准中规定的正常流排版逻辑。
  • 最后的正常流应用部分,我以两个经典布局问题等分布局和自适应宽为例,为你讲解了正常流实际使用的一些技巧。

35 | CSS Flex排版:为什么垂直居中这么难?

Flex 的设计

Flex 在英文中是可伸缩的意思,一些翻译会把它译作弹性

Flex 排版的核心是 display:flex 和 flex 属性,它们配合使用。具有 display:flex 的元素我们称为 flex 容器,它的子元素或者盒被称作 flex 项。

flex 项如果有 flex 属性,会根据 flex 方向代替宽 / 高属性,形成“填补剩余尺寸”的特性,这是一种典型的“根据外部容器决定内部尺寸”的思路,也是我们最常用的 Windows 和 Apple 窗口系统的设计思路。

Flex 的原理

  • 第一步是把 flex 项分行,有 Flex 属性的 flex 项可以暂且认为主轴尺寸为 0,所以,它可以一定放进当前行。
  • 第二步我们来计算每个 flex 项主轴尺寸和位置。
  • 第三步我们来计算 flex 项的交叉轴尺寸和位置。

Flex 的应用

接下来我们来尝试用 flex 排版来解决一下当年的 CSS 三大经典问题(简直易如反掌)。

  • 垂直居中
  • 两列等高
  • 自适应宽

总结

我们先从感性的角度,介绍了 flex 的设计,flex 的设计是一种不同于流布局的,自外而内的设计思路。

接下来我们讲解了 flex 的实现原理,也就是具体的排版算法。要想理解 flex 排版的原理,主轴和交叉轴是非常重要的抽象,flex 排版三个步骤:分行、计算主轴、计算交叉轴。

最后我们给出了几个例子,解决了旧时代的 CSS 三大经典问题。


38 | CSS动画与交互:为什么动画要用贝塞尔曲线这么奇怪的东西?

CSS 中跟动画相关的属性有两个:animation 和 transition。

animation 属性

@keyframes mykf
{
  from {background: red;}
  to {background: yellow;}
}

div
{
    animation:mykf 5s infinite;
}

这里展示了 animation 的基本用法,实际上 animation 分成六个部分:

  • animation-name 动画的名称,这是一个 keyframes 类型的值(我们在第 9 讲“CSS 语法:除了属性和选择器,你还需要知道这些带 @的规则”讲到过,keyframes 产生一种数据,用于定义动画关键帧);
  • animation-duration 动画的时长;
  • animation-timing-function 动画的时间曲线;
  • animation-delay 动画开始前的延迟;
  • animation-iteration-count 动画的播放次数;
  • animation-direction 动画的方向。

transition 属性

接下来我们来介绍一下 transition。transition 与 animation 相比来说,是简单得多的一个属性。

  • transition-property 要变换的属性;
  • transition-duration 变换的时长;
  • transition-timing-function 时间曲线;
  • transition-delay 延迟。

这里的四个部分,可以重复多次,指定多个属性的变换规则。

实际上,有时候我们会把 transition 和 animation 组合,抛弃 animation 的 timing-function,以编排不同段用不同的曲线。

@keyframes mykf {
  from { top: 0; transition:top ease}
  50% { top: 30px;transition:top ease-in }
  75% { top: 10px;transition:top ease-out }
  to { top: 0; transition:top linear}
}

在这个例子中,在 keyframes 中定义了 transition 属性,以达到各段曲线都不同的效果。

三次贝塞尔曲线

我们在这里首先要了解一下贝塞尔曲线,贝塞尔曲线是一种差值曲线,它描述了两个点之间差值来形成连续的曲线形状的规则。

贝塞尔曲线拟合

理论上,贝塞尔曲线可以通过分段的方式拟合任意曲线,但是有一些特殊的曲线,是可以用贝塞尔曲线完美拟合的,比如抛物线。

总结

我们今天的课程,重点介绍了动画和它背后的一些机制。

CSS 用 transition 和 animation 两个属性来实现动画,这两个属性的基本用法很简单,我们今天还介绍了它们背后的原理:贝塞尔曲线。

我们中介绍了贝塞尔曲线的实现原理和贝塞尔曲线的拟合技巧。


40 | CSS渲染:CSS是如何绘制颜色的?

颜色的原理

  • RGB 颜色

我们在计算机中,最常见的颜色表示法是 RGB 颜色,它符合光谱三原色理论:红、绿、蓝三种颜色的光可以构成所有的颜色。

  • CMYK 颜色

品红、黄、青、黑

  • HSL 颜色

我们刚才讲的颜色是从人类的视觉原理建模,应该说是十分科学了。但是,人类对颜色的认识却并非来自自己的神经系统,当我们把阳光散射,可以得到七色光:红橙黄绿蓝靛紫,实际上,阳光接近白光,它包含了各种颜色的光,它散射之后,应该是个基本连续的。这说明对人的感知来说,颜色远远大于红、绿、蓝。

色相(H)。加上颜色的纯度(S)和明度(L),就构成了一种颜色的表示

其它颜色

接下来我们讲一讲 RGBA,RGBA 是代表 Red(红色)、Green(绿色)、Blue(蓝色)和 Alpha 的色彩空间。RGBA 颜色被用来表示带透明度的颜色,实际上,Alpha 通道类似一种颜色值的保留字。在 CSS 中,Alpha 通道被用于透明度,所以我们的颜色表示被称作 RGBA,而不是 RGBO(Opacity)。

为了方便使用,CSS 还规定了名称型的颜色,它内置了大量(140 种)的颜色名称。不过这里我要挑出两个颜色来讲一讲:金(gold)和银(silver)。

渐变

在 CSS 中,background-image 这样的属性,可以设为渐变。CSS 中支持两种渐变,一种是线性渐变,一种是放射性渐变,我们先了解一下它们的基本用法:

线性渐变的写法是:

linear-gradient(direction, color-stop1, color-stop2, ...);

放射性渐变需要一个中心点和若干个颜色:

radial-gradient(shape size at position, start-color, ..., last-color);

形状

CSS 中的很多属性还会产生形状,比如我们常见的属性:

  • border
  • box-shadow
  • border-radius

这些产生形状的属性非常有趣,我们也能看到很多利用它们来产生的 CSS 黑魔法。然而,这里我有一个相反的建议,我们仅仅把它们用于基本的用途,把 border 用于边框、把 box-shadow 用于阴影,把 border-radius 用于圆角,所有其它的场景,都有一个更好的替代品:datauri+svg。

总结

今天我们介绍了 CSS 中渲染相关的属性:颜色和形状。

我们重点介绍了 CSS 的颜色系统,从颜色基本原理讲解了 RGB 颜色、CMYK 颜色和 HSV 颜色,我们还讲解了 Alpha 通道。

接下来我们又讲了颜色的一个重要应用:渐变,我们可以把渐变看作是一个更复杂的颜色,它非常实用,能够用渐变绘制很多的图像。

最后我们讲解了形状相关的属性,以及 SVG 应用的一个小技巧。


41 | CSS小实验:动手做,用代码挖掘CSS属性

浏览器中已经实现的属性

Object.keys(document.body.style).filter(e => !e.match(/^webkit/))

这段代码思路非常简单,就是枚举 document.body.style 上的所有属性,并且去掉 webkit 前缀的私有属性。

小实验:找出 W3C 标准中的 CSS 属性

我们这个爬虫的思路是:用 iframe 来加载所有标准的网页,然后用 JavaScript 找出它们中间定义的属性。

  • 第一步:找到 CSS 相关的标准。
  • 第二步:分析每个标准中的 CSS 属性。

你可能感兴趣的:(编程语言,大前端)