This commit is contained in:
louzefeng
2024-07-09 18:38:56 +00:00
parent 8bafaef34d
commit bf99793fd0
6071 changed files with 1017944 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
<audio id="audio" title="CSS Flex排版为什么垂直居中这么难" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/52/38/52193c48c2cd619ee60343b5c478d738.mp3"></audio>
你好我是winter。今天我们来谈谈Flex排版。
我们在前面多次讲过正常流排版的设计来源于数百年来出版行业的排版经验而HTML诞生之初也确实是作为一种“超文本”存在的。
但是自上世纪90年代以来Web标准和各种Web应用蓬勃发展网页的功能逐渐从“文本信息”向着“软件功能”过渡这个思路的变化导致了CSS的正常流逐渐不满足人民群众的需求了。
这是因为文字排版的思路是“改变文字和盒的相对位置,把它放进特定的版面中”,而软件界面的思路则是“改变盒的大小,使得它们的结构保持固定”。
因此在早年的CSS中“使盒按照外部尺寸变化”的能力非常弱。在我入行前端的时间大约2006年CSS三大经典问题垂直居中问题两列等高问题自适应宽问题。这是在其它UI系统中最为基本的问题而到了CSS中却变成了困扰工程师的三座大山。
机智的前端开发者们曾经创造了各种黑科技来解决问题包括著名的table布局、负margin、float与clear等等。在这种情况下Flex布局被随着CSS3一起提出最初叫box布局可以说是解决了大问题。
React Native则更为大胆地使用了纯粹的Flex排版不再支持正常流最终也很好地支持了大量的应用界面布局这一点也证明了Flex排版的潜力。
今天我们就从设计、原理和应用三个方面来学习一下Flex布局我们先从设计开始。
## Flex的设计
Flex在英文中是可伸缩的意思一些翻译会把它译作弹性我觉得有点不太准确但是确实中文中没有更好的词。
Flex排版的核心是display:flex和flex属性它们配合使用。具有display:flex的元素我们称为flex容器它的子元素或者盒被称作flex项。
flex项如果有flex属性会根据flex方向代替宽/高属性形成“填补剩余尺寸”的特性这是一种典型的“根据外部容器决定内部尺寸”的思路也是我们最常用的Windows和Apple窗口系统的设计思路。
## Flex的原理
说完了设计我们再来看看原理Flex的实现并不复杂我曾经写过一个基本实现提交给spritejs项目代码可以[参考这里](https://github.com/spritejs/sprite-core/commit/8757b4d3888b4f237b1089e94e075ab58ca952a6#diff-677d382da9f8d81f61d50af24f937b32R32)。
下面我们就来讲解一下如何实现一个Flex布局。
首先Flex布局支持横向和纵向这样我们就需要做一个抽象我们把Flex延伸的方向称为“主轴”把跟它垂直的方向称为“交叉轴”。这样flex项中的width和height就会称为交叉轴尺寸或者主轴尺寸。
而Flex又支持反向排布这样我们又需要抽象出交叉轴起点、交叉轴终点、主轴起点、主轴终点它们可能是top、left、bottom、right。
Flex布局中有一种特殊的情况那就是flex容器没有被指定主轴尺寸这个时候实际上Flex属性完全没有用了所有Flex尺寸都可以被当做0来处理Flex容器的主轴尺寸等于其它所有flex项主轴尺寸之和。
接下来我们开始做Flex排版。
**第一步是把flex项分行有flex属性的flex项可以暂且认为主轴尺寸为0所以它可以一定放进当前行。**
接下来我们把flex项逐个放入行不允许换行的话我们就“无脑地”把flex项放进同一行。允许换行的话我们就先设定主轴剩余空间为Flex容器主轴尺寸每放入一个就把主轴剩余空间减掉它的主轴尺寸直到某个flex项放不进去为止换下一行重复前面动作。
分行过程中,我们会顺便对每一行计算两个属性:交叉轴尺寸和主轴剩余空间,交叉轴尺寸是本行所有交叉轴尺寸的最大值,而主轴剩余空间前面已经说过。
**第二步我们来计算每个flex项主轴尺寸和位置。**
如果Flex容器是不允许换行的并且最后主轴尺寸超出了Flex容器就要做等比缩放。
如果Flex容器有多行那么根据我们前面的分行算法必然有主轴剩余空间这时候我们要找出本行所有的带Flex属性的flex项把剩余空间按Flex比例分给它们即可。
做好之后我们就可以根据主轴排布方向确定每个flex项的主轴位置坐标了。
如果本行完全没有带flex属性的flex项justify-content机制就要生效了它的几个不同的值会影响剩余空白如何分配作为实现者我们只要在计算flex项坐标的时候加上一个数值即可。
例如如果是flex-start就要加到第一个flex项身上如果是center就给第一个flex项加一半的尺寸如果是space-between就要给除了第一个以外的每个flex项加上“flex项数减一分之一”。
**第三步我们来计算flex项的交叉轴尺寸和位置。**
交叉轴的计算首先是根据align-content计算每一行的位置这部分跟justify-content非常类似。
再根据alignItems和flex项的alignSelf来确定每个元素在行内的位置。
计算完主轴和交叉轴每个flex项的坐标、尺寸就都确定了这样我们就完成了整个的Flex布局。
## Flex的应用
接下来我们来尝试用flex排版来解决一下当年的CSS三大经典问题简直易如反掌
垂直居中:
```
&lt;div id="parent"&gt;
&lt;div id="child"&gt;
&lt;/div&gt;
&lt;/div&gt;
```
```
#parent {
display:flex;
width:300px;
height:300px;
outline:solid 1px;
justify-content:center;
align-content:center;
align-items:center;
}
#child {
width:100px;
height:100px;
outline:solid 1px;
}
```
思路是创建一个只有一行的flexbox然后用align-items:center;和align-content:center;来保证行位于容器中,元素位于行中。
两列等高:
```
&lt;div class="parent"&gt;
&lt;div class="child" style="height:300px;"&gt;
&lt;/div&gt;
&lt;div class="child"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;br/&gt;
&lt;div class="parent"&gt;
&lt;div class="child" &gt;
&lt;/div&gt;
&lt;div class="child" style="height:300px;"&gt;
&lt;/div&gt;
&lt;/div&gt;
```
```
.parent {
display:flex;
width:300px;
justify-content:center;
align-content:center;
align-items:stretch;
}
.child {
width:100px;
outline:solid 1px;
}
```
思路是创建一个只有一行的flexbox然后用stretch属性让每个元素高度都等于行高。
自适应宽:
```
&lt;div class="parent"&gt;
&lt;div class="child1"&gt;
&lt;/div&gt;
&lt;div class="child2"&gt;
&lt;/div&gt;
&lt;/div&gt;
```
```
.parent {
display:flex;
width:300px;
height:200px;
background-color:pink;
}
.child1 {
width:100px;
background-color:lightblue;
}
.child2 {
width:100px;
flex:1;
outline:solid 1px;
}
```
这个就是Flex设计的基本能力了给要自适应的元素添加flex属性即可。
## 总结
今天我们从Flex的设计、原理和应用三个方面一起学习了Flex排版。
我们先从感性的角度介绍了Flex的设计Flex的设计是一种不同于流布局的自外而内的设计思路。
接下来我们讲解了Flex的实现原理也就是具体的排版算法。要想理解Flex排版的原理主轴和交叉轴是非常重要的抽象Flex排版三个步骤分行、计算主轴、计算交叉轴。
最后我们给出了几个例子解决了旧时代的CSS三大经典问题。
最后给你留一个小问题请根据我的代码和文字编写一段使用“position:absolute”来模拟Flex布局的js。大家可以根据自己的水平简化需求比如可以实现一个仅仅支持横向的、单行的所有flex项必须指定高度的Flex布局。

View File

@@ -0,0 +1,226 @@
<audio id="audio" title="CSS 选择器如何选中svg里的a元素" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/1f/d9/1ffed97a32a6015e9a4b28ff83356cd9.mp3"></audio>
你好我是winter。
我们在之前CSS语法课程中已经介绍了关于选择器的一部分基础知识。在今天的这一课里我们来系统学习一下CSS选择器。
在CSS语法课程中我们已经见过一些选择器了但在进入到具体的选择器介绍之前我们首先要对选择器有一个整体的认识。
我先来讲讲选择器是什么选择器是由CSS最先引入的一个机制但随着document.querySelector等API的加入选择器已经不仅仅是CSS的一部分了。我们今天这一课就重点讲讲CSS选择器的一些机制。
**选择器的基本意义是:根据一些特征,选中元素树上的一批元素。**
我们把选择器的结构分一下类,那么由简单到复杂可以分成以下几种。
- 简单选择器:针对某一特征判断是否选中元素。
- 复合选择器:连续写在一起的简单选择器,针对元素自身特征选择单个元素。
- 复杂选择器:由“(空格)”“ &gt;”“ ~”“ +”“ ||”等符号连接的复合选择器,根据父元素或者前序元素检查单个元素。
- 选择器列表:由逗号分隔的复杂选择器,表示“或”的关系。
我们可以看到,选择器是由简单选择器逐级组合而成的结构,那么我们就来首先看一下简单选择器。
## 简单选择器
我们在前面说过,简单选择器是针对某一特征判断是否为选中元素。今天我会为你介绍一系列常见的简单选择器,我们把相似的简单选择器放在一起,这样更易于你去记忆。
<img src="https://static001.geekbang.org/resource/image/4c/ce/4c9ac78870342dc802137ea9c848c0ce.png" alt="">
## 类型选择器和全体选择器
我们要介绍的第一个简单选择器就是类型选择器,它根据一个元素的标签名来选中元素。
比如:
```
div {
}
```
这看上去非常简单但是实际上我们还必须要考虑HTML或者XML元素的命名空间问题。
比如我们的svg元素实际上在 [http://www.w3.org/2000/svg](http://www.w3.org/2000/svg) 命名空间之下。
svg和HTML中都有a元素我们若要想区分选择svg中的a和HTML中的a就必须用带命名空间的类型选择器。
```
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;utf-8&quot;&gt;
&lt;title&gt;JS Bin&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;svg width=&quot;100&quot; height=&quot;28&quot; viewBox=&quot;0 0 100 28&quot; version=&quot;1.1&quot;
xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;&gt;
&lt;desc&gt;Example link01 - a link on an ellipse
&lt;/desc&gt;
&lt;a xlink:href=&quot;http://www.w3.org&quot;&gt;
&lt;text y=&quot;100%&quot;&gt;name&lt;/text&gt;
&lt;/a&gt;
&lt;/svg&gt;
&lt;br/&gt;
&lt;a href=&quot;javascript:void 0;&quot;&gt;name&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;
@namespace svg url(http://www.w3.org/2000/svg);
@namespace html url(http://www.w3.org/1999/xhtml);
svg|a {
stroke:blue;
stroke-width:1;
}
html|a {
font-size:40px
}
```
这里有一个特殊的选择器,就是“ * ” ,它称为全体选择器,可以选中任意元素。它的用法跟类型选择器是完全一致的,这里就把它们放到一起介绍了。
## id选择器与class选择器
id选择器和class选择器都是针对特定属性的选择器。id选择器是“#”号后面跟随id名class选择器是“.”后面跟随class名。我们来看看基本用法
```
#myid {
stroke:blue;
stroke-width:1;
}
.mycls {
font-size:40px
}
```
这两个选择器都是在属性选择器之前就设计出来的选择器属性选择器出来了以后理论上可以一定程度上替代它们。但是要注意class选择器识别的是用空格分隔的class语法。
```
&lt;a class=&quot;a b c&quot;&gt;xxx&lt;/a&gt;
.a {
color:red;
}
```
在这个例子中我们使用了用空格分隔的class属性使用“.a”“.b”或者“.c”都能够选中元素也可以使用多个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语法设计的是比较复杂的我们这里仅仅介绍基本用法。我们还是看几个例子
<img src="https://static001.geekbang.org/resource/image/1e/a9/1ebdba2978a22c13844d108318b271a9.png" alt="">
- :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 伪类。
这个伪类是个函数型伪类,它的作用时选中内部的简单选择器命中的元素。
```
*|*:not(:hover)
```
选择器3级标准中not只支持简单选择器在选择器4级标准则允许not接受一个选择器列表这意味着选择器支持嵌套仅靠not即可完成选择器的一阶真值逻辑完备但目前还没有看到浏览器实现它。
在Selector Level 4草案中还引入了:is :where :has 等逻辑伪类但是它们有一些违背了选择器匹配DOM树不回溯的原则所以这部分设计最终的命运如何还不太确定。
### 其它伪类选择器
还有一些草案中或者不常用的选择器,你仅做大概了解即可。
<li>
国际化:用于处理国际化和多语言问题。
<ul>
- dir
- lang
音频/视频:用于区分音视频播放状态。
- play
- pause
时序:用于配合读屏软件等时序性客户端的伪类。
- current
- past
- future
表格用于处理table的列的伪类。
- nth-col
- nth-last-col
伪类是很大的一类简单选择器它是选择器能力的一种补充。在实际使用中我还是建议你尽量通过合适的id和class来标识元素约束伪类的使用。最好只在不得不使用伪类的场景使用伪类这对于CSS代码的性能和可读性都有好处。
## 结语
这一节课程中我们介绍了CSS选择器的整体结构并且介绍了一系列简单选择器。它们包括了下面这些内容。
- 类型选择器:根据一个元素的标签名来选中元素。
- 全体选择器:与类型选择器类似,选择任意元素。
- id选择器#后面跟随id名
- class选择器.后面跟随class名。
- 伪类选择器一系列由CSS规定好的选择器它们以冒号开头伪类有普通型和函数型。
在下一节课,我们开始进入到更复杂的情况,我们将会介绍选择器的组合使用方式和选择器的一些机制。
今天留给你的思考题是用JavaScript实现一个能够处理所有简单选择器的querySelector行为伪类除外你可以把你的答案分享出来我们一起来探讨吧。

View File

@@ -0,0 +1,295 @@
<audio id="audio" title="CSS动画与交互为什么动画要用贝塞尔曲线这么奇怪的东西" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/e3/15/e3494e8a6911d81c2970dcabc4597e15.mp3"></audio>
你好我是winter今天我们来学习一下CSS的动画和交互。
在CSS属性中有这么一类属性它负责的不是静态的展现而是根据用户行为产生交互。这就是今天我们要讲的属性。
首先我们先从属性来讲起。CSS中跟动画相关的属性有两个animation和transition。
## animation属性和transition属性
我们先来看下animation的示例通过示例来了解一下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 动画的方向。
我们先来看 `animation-name`这个是一个keyframes类型需要配合@规则来使用
比如,我们前面的示例中,就必须配合定义 mymove 这个 keyframes。keyframes的主体结构是一个名称和花括号中的定义它按照百分比来规定数值例如
```
@keyframes mykf {
0% { top: 0; }
50% { top: 30px; }
75% { top: 10px; }
100% { top: 0; }
}
```
这里我们可以规定在开始时把top值设为0在50%是设为30px在75%时设为10px到100%时重新设为0这样动画执行时就会按照我们指定的关键帧来变换数值。
这里0%和100%可以写成from和to不过一般不会混用画风会变得很奇怪比如
```
@keyframes mykf {
from { top: 0; }
50% { top: 30px; }
75% { top: 10px; }
to { top: 0; }
}
```
这里关键帧之间,是使用 `animation-timing-function` 作为时间曲线的,稍后我会详细介绍时间曲线。
接下来我们来介绍一下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属性以达到各段曲线都不同的效果。
接下来我们就来详细讲讲刚才提到的timing-function动画的时间曲线。
## 三次贝塞尔曲线
我想你能从很多CSS的资料中都找到了贝塞尔曲线但是为什么CSS的时间曲线要选用三次贝塞尔曲线呢
我们在这里首先要了解一下贝塞尔曲线,贝塞尔曲线是一种插值曲线,它描述了两个点之间差值来形成连续的曲线形状的规则。
一个量(可以是任何矢量或者标量)从一个值到变化到另一个值,如果我们希望它按照一定时间平滑地过渡,就必须要对它进行插值。
最基本的情况,我们认为这个变化是按照时间均匀进行的,这个时候,我们称其为线性插值。而实际上,线性插值不大能满足我们的需要,因此数学上出现了很多其它的插值算法,其中贝塞尔插值法是非常典型的一种。它根据一些变换中的控制点来决定值与时间的关系。
贝塞尔曲线是一种被工业生产验证了很多年的曲线,它最大的特点就是“平滑”。时间曲线平滑,意味着较少突兀的变化,这是一般动画设计所追求的。
贝塞尔曲线用于建筑设计和工业设计都有很多年历史了,它最初的应用是汽车工业用贝塞尔曲线来设计车型。
K次贝塞尔插值算法需要k+1个控制点最简单的一次贝塞尔插值就是线性插值将时间表示为0到1的区间一次贝塞尔插值公式是
<img src="https://static001.geekbang.org/resource/image/d7/f8/d7e7c3bcc1e2b2ce72fde79956e872f8.png" alt="">
“二次贝塞尔插值”有3个控制点相当于对P0和P1P1和P2分别做贝塞尔插值再对结果做一次贝塞尔插值计算
<img src="https://static001.geekbang.org/resource/image/14/84/14d6a5396b7c0cc696c52a9e06e45184.png" alt="">
“三次贝塞尔插值”则是“两次‘二次贝塞尔插值’的结果,再做一次贝塞尔插值”:
<img src="https://static001.geekbang.org/resource/image/65/b2/65ff1dd9b8e5911f9dd089531acea2b2.png" alt="">
贝塞尔曲线的定义中带有一个参数t但是这个t并非真正的时间实际上贝塞尔曲线的一个点(x, y)这里的x轴才代表时间。
这就造成了一个问题如果我们使用贝塞尔曲线的直接定义是没办法直接根据时间来计算出数值的因此浏览器中一般都采用了数值算法其中公认做有效的是牛顿积分我们可以看下JavaScript版本的代码
```
function generate(p1x, p1y, p2x, p2y) {
const ZERO_LIMIT = 1e-6;
// Calculate the polynomial coefficients,
// implicit first and last control points are (0,0) and (1,1).
const ax = 3 * p1x - 3 * p2x + 1;
const bx = 3 * p2x - 6 * p1x;
const cx = 3 * p1x;
const ay = 3 * p1y - 3 * p2y + 1;
const by = 3 * p2y - 6 * p1y;
const cy = 3 * p1y;
function sampleCurveDerivativeX(t) {
// `ax t^3 + bx t^2 + cx t' expanded using Horner 's rule.
return (3 * ax * t + 2 * bx) * t + cx;
}
function sampleCurveX(t) {
return ((ax * t + bx) * t + cx ) * t;
}
function sampleCurveY(t) {
return ((ay * t + by) * t + cy ) * t;
}
// Given an x value, find a parametric value it came from.
function solveCurveX(x) {
var t2 = x;
var derivative;
var x2;
// https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
// First try a few iterations of Newton's method -- normally very fast.
// http://en.wikipedia.org/wiki/Newton's_method
for (let i = 0; i &lt; 8; i++) {
// f(t)-x=0
x2 = sampleCurveX(t2) - x;
if (Math.abs(x2) &lt; ZERO_LIMIT) {
return t2;
}
derivative = sampleCurveDerivativeX(t2);
// == 0, failure
/* istanbul ignore if */
if (Math.abs(derivative) &lt; ZERO_LIMIT) {
break;
}
t2 -= x2 / derivative;
}
// Fall back to the bisection method for reliability.
// bisection
// http://en.wikipedia.org/wiki/Bisection_method
var t1 = 1;
/* istanbul ignore next */
var t0 = 0;
/* istanbul ignore next */
t2 = x;
/* istanbul ignore next */
while (t1 &gt; t0) {
x2 = sampleCurveX(t2) - x;
if (Math.abs(x2) &lt; ZERO_LIMIT) {
return t2;
}
if (x2 &gt; 0) {
t1 = t2;
} else {
t0 = t2;
}
t2 = (t1 + t0) / 2;
}
// Failure
return t2;
}
function solve(x) {
return sampleCurveY(solveCurveX(x));
}
return solve;
}
```
这段代码其实完全翻译自WebKit的C++代码,牛顿积分的具体原理请参考相关数学著作,注释中也有相关的链接。
这个JavaScript版本的三次贝塞尔曲线可以用于实现跟CSS一模一样的动画。
## 贝塞尔曲线拟合
理论上,贝塞尔曲线可以通过分段的方式拟合任意曲线,但是有一些特殊的曲线,是可以用贝塞尔曲线完美拟合的,比如抛物线。
这里我做了一个示例,用于模拟抛物线:
```
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
&lt;meta name="viewport" content="width=device-width"&gt;
&lt;title&gt;Simulation&lt;/title&gt;
&lt;style&gt;
.ball {
width:10px;
height:10px;
background-color:black;
border-radius:5px;
position:absolute;
left:0;
top:0;
transform:translateY(180px);
}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;label&gt;运动时间:&lt;input value="3.6" type="number" id="t" /&gt;s&lt;/label&gt;&lt;br/&gt;
&lt;label&gt;初速度:&lt;input value="-21" type="number" id="vy" /&gt; px/s&lt;/label&gt;&lt;br/&gt;
&lt;label&gt;水平速度:&lt;input value="21" type="number" id="vx" /&gt; px/s&lt;/label&gt;&lt;br/&gt;
&lt;label&gt;重力:&lt;input value="10" type="number" id="g" /&gt; px/s²&lt;/label&gt;&lt;br/&gt;
&lt;button onclick="createBall()"&gt;来一个球&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
```
```
function generateCubicBezier (v, g, t){
var a = v / g;
var b = t + v / g;
return [[(a / 3 + (a + b) / 3 - a) / (b - a), (a * a / 3 + a * b * 2 / 3 - a * a) / (b * b - a * a)],
[(b / 3 + (a + b) / 3 - a) / (b - a), (b * b / 3 + a * b * 2 / 3 - a * a) / (b * b - a * a)]];
}
function createBall() {
var ball = document.createElement("div");
var t = Number(document.getElementById("t").value);
var vx = Number(document.getElementById("vx").value);
var vy = Number(document.getElementById("vy").value);
var g = Number(document.getElementById("g").value);
ball.className = "ball";
document.body.appendChild(ball)
ball.style.transition = `left linear ${t}s, top cubic-bezier(${generateCubicBezier(vy, g, t)}) ${t}s`;
setTimeout(function(){
ball.style.left = `${vx * t}px`;
ball.style.top = `${vy * t + 0.5 * g * t * t}px`;
}, 100);
setTimeout(function(){ document.body.removeChild(ball); }, t * 1000);
}
```
这段代码中,我实现了抛物线运动的小球,其中核心代码就是 generateCubicBezier 函数。
这个公式完全来自于一篇论文,推理过程我也不清楚,但是不论如何,它确实能够用于模拟抛物线。
实际上,我们日常工作中,如果需要用贝塞尔曲线拟合任何曲线,都可以找到相应的论文,我们只要取它的结论即可。
## 总结
我们今天的课程,重点介绍了动画和它背后的一些机制。
CSS用transition和animation两个属性来实现动画这两个属性的基本用法很简单我们今天还介绍了它们背后的原理贝塞尔曲线。
我们中介绍了贝塞尔曲线的实现原理和贝塞尔曲线的拟合技巧。
最后留给你一个小问题请纯粹用JavaScript来实现一个transition函数用它来跟CSS的transition来做一下对比看看有哪些区别。

View File

@@ -0,0 +1,265 @@
<audio id="audio" title="CSS排版从毕升开始我们就开始用正常流了" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/29/21/29e2a41c0ffce8e903024d390d4aaf21.mp3"></audio>
你好我是winter。今天我们来聊聊CSS的正常流。
我想在CSS中大家最讨厌的大概就是排版部分了。因为早年的CSS设计上不能够很好地支持软件排版需求导致大家需要使用很多黑科技让很多新人望而却步。
现在CSS提供了很多种排版方式我们有很多选项可以选择自己适合的那一种然而正常流却是我们绕不开的一种排版。
我们能够在网上看到关于正常流的各种资料比如块级格式化上下文、margin折叠等等……这一系列的概念光是听起来就令人非常头痛。
所以我相信很多同学一定会奇怪:正常流到底正常在哪里。事实上,我认为正常流本身是简单和符合直觉的东西。
我们之所以会觉得它奇怪是因为如果我们从严苛的CSS标准角度去理解正常流规定排版的算法就需要引入上述那些复杂的概念。但是如果我们单纯地从感性认知的层面去理解正常流它其实是简单的。
下面,就让我们先抛弃掉所有的已知概念,从感性认知的角度出发,一起去理解一下正常流。
## 正常流的行为
首先,我们先从词源来讲一讲排版这件事。
在毕昇发明活字印刷之前,排版这项工作是不存在的,相应的操作叫做“雕版”。人们要想印刷书籍,就需要依靠雕版工人去手工雕刻印版。
活字印刷的出现将排版这个词引入进来排版是活字印刷的15道工序之一不论是古代的木质活字印刷还是近代的铅质活字印刷排版的过程是由排版工人一个字一个字从字架捡出再排入版框中。实际上这个过程就是一个流式处理的过程。
从古代活字印刷开始到现代的出版行业再到今天的Web排版过程其实并没有什么本质的变化只不过今天在我们的CSS中排版需要处理的内容不再是简单的大小相同的木字或者铅字而是有着不同字体和字号的富文本以及插入在富文本中大小不等的盒。
并且,在这些过程中,都会有一个正常流的存在。那么,正常流是什么样的呢?
**我们可以用一句话来描述正常流的排版行为,那就是:依次排列,排不下了换行。**这个操作很简单吧,我想,任何一个不懂排版的人都会将其作为排版时的第一反应。
理解了正常流的基本概念,剩下的功能只需要在它的基础上延伸一下就好。
在正常流基础上我们有float相关规则使得一些盒占据了正常流需要的空间我们可以把float理解为“文字环绕”。
<img src="https://static001.geekbang.org/resource/image/af/65/aff7250eac6064158021aea86dd4ac65.png" alt="">
我们还有vertical-align相关规则规定了如何在垂直方向对齐盒。vertical-align相关规则看起来复杂但是实际上基线、文字顶/底、行顶/底都是我们正常书写文字时需要用到的概念,只是我们平时不一定会总结它们。
下图展示了在不同的vertical-align设置时盒与文字是如何混合排版的。为了方便你理解我们用代码给大家标注了基线、文字顶/底、行顶/底等概念。
<img src="https://static001.geekbang.org/resource/image/aa/e3/aa6611b00f71f606493f165294410ee3.png" alt=""><br>
(点击大图查看)
除此之外margin折叠是很多人非常不理解的一种设计但是实际上我们可以把margin理解为“一个元素规定了自身周围至少需要的空间”这样我们就非常容易理解为什么margin需要折叠了。
## 正常流的原理
我们前面描述了正常流的行为,接下来我们要切换一下模式,用比较严谨的姿势来理解一下正常流。
在CSS标准中规定了如何排布每一个文字或者盒的算法这个算法依赖一个排版的“当前状态”CSS把这个当前状态称为“格式化上下文formatting context”。
我们可以认为排版过程是这样的:
>
格式化上下文 + 盒/文字 = 位置
>
formatting context + boxes/charater = positions
我们需要排版的盒,是分为块级盒和行内级盒的,所以排版需要分别为它们规定了块级格式化上下文和行内级格式化上下文。
与正常流一样,如果我们单纯地看格式化上下文,规则其实是非常简单的。
块级格式化上下文顺次排列元素:
<img src="https://static001.geekbang.org/resource/image/a5/e7/a5e1b9a77d9745499f96d25cf0a0dbe7.png" alt="">
行内级格式化上下文顺次排列元素:
<img src="https://static001.geekbang.org/resource/image/1c/cf/1ced4fa809b30343df45e559cf0c08cf.png" alt="">
注意,块级和行内级元素的排版,受文字书写方向的影响,这里我们讲上下左右只是为了方便你直观理解。
当我们要把正常流中的一个盒或者文字排版,需要分成三种情况处理。
- **当遇到块级盒**:排入块级格式化上下文。
- **当遇到行内级盒或者文字**:首先尝试排入行内级格式化上下文,如果排不下,那么创建一个行盒,先将行盒排版(行盒是块级,所以到第一种情况),行盒会创建一个行内级格式化上下文。
- **遇到float盒**把盒的顶部跟当前行内级上下文上边缘对齐然后根据float的方向把盒的对应边缘对到块级格式化上下文的边缘之后重排当前行盒。
我们以上讲的都是一个块级格式化上下文中的排版规则,实际上,页面中的布局没有那么简单,一些元素会在其内部创建新的块级格式化上下文,这些元素有:
1. 浮动元素;
1. 绝对定位元素;
1. 非块级但仍能包含块级元素的容器如inline-blocks, table-cells, table-captions
1. 块级的能包含块级元素的容器且属性overflow不为visible。
这里的最后一条比较绕,实际上,我个人喜欢用另一种思路去理解它:
自身为块级且overflow为visible的块级元素容器它的块级格式化上下文和外部的块级格式化上下文发生了融合也就是说如果不考虑盒模型相关的属性这样的元素从排版的角度就好像根本不存在。
好了,到这里我们已经讲完了正常流的排版详细规则,但是理解规则仅仅是基础,我们还需要掌握一些技巧。
## 正常流的使用技巧
现在,我们就一起来动手用实际的例子来研究一下。我们今天来看看等分布局和自适应宽,从这两种经典布局问题入手,一起来探索一下正常流的使用技巧。
### 等分布局问题
横向等分布局是一个很常见的需求,按照一般的思路,我们可以使用百分比宽度来解决,我们参考以下代码:
```
&lt;div class=&quot;outer&quot;&gt;
&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
.inner {
width:33.33%;
height:300px;
display:inline-block;
outline:solid 1px blue;
}
```
在这段HTML代码中我们放了三个div用CSS给它们指定了百分比宽度并且指定为inline-block。
但是这段代码执行之后效果跟我们预期不同我们可以发现每个div并非紧挨中间有空白这是因为我们为了代码格式加入的换行和空格被HTML当作空格文本跟inline盒混排了的缘故。
解决方案是修改HTML代码去掉空格和换行
```
&lt;div class=&quot;outer&quot;&gt;&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;&lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;&lt;/div&gt;
```
但是这样做影响了源代码的可读性一个变通的方案是改变outer中的字号为0。
```
.inner {
width:33.33%;
height:300px;
display:inline-block;
outline:solid 1px blue;
font-size:30px;
}
.outer {
font-size:0;
}
```
在某些浏览器中因为像素计算精度问题还是会出现换行我们给outer添加一个特定宽度
```
.inner {
width:33.33%;
height:300px;
display:inline-block;
outline:solid 1px blue;
}
.outer {
width:101px
}
```
这个代码在某些旧版本浏览器中会出现换行。为了保险起见我们给最后一个div加上一个负的右margin
```
.outer {
width:101px
}
.inner {
width:33.33%;
height:300px;
display:inline-block;
outline:solid 1px blue;
}
.inner:last-child {
margin-right:-5px;
}
```
这样就可以解决旧版本浏览器的问题了。
除了使用inline-blockfloat也可以实现类似的效果但是float元素只能做顶对齐不如inline-block灵活。
### 自适应宽
我们再来说说自适应宽。在IE6统治浏览器市场的旧时代自适应宽一个元素固定宽度另一个元素填满父容器剩余宽度是个经典的布局问题我们现在就看一下如何使用正常流来解决。
我们首先来看一下问题。
```
&lt;div class=&quot;outer&quot;&gt;
&lt;div class=&quot;fixed&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;auto&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
.fixed {
width:200px;
}
.fixed, .auto {
height:300px;
outline:solid 1px blue;
}
```
这里fixed这个div宽度已经被指定好我们需要添加css代码尝试让.auto填满剩余宽度。
使用正常流解决这个问题的思路是利用负margin
```
.fixed {
display:inline-block;
vertical-align:top;
}
.auto {
margin-left:-200px;
width:100%;
display:inline-block;
vertical-align:top;
}
```
但是这样做会导致auto中的内容位置不对所以我们还需要使用padding把内容挤出来最终完整代码如下
```
.fixed {
display:inline-block;
vertical-align:top;
}
.auto {
margin-left:-200px;
padding-left:200px;
box-sizing:border-box;
width:100%;
display:inline-block;
vertical-align:top;
}
```
这样就给auto添加了padding-left和box-sizing两个属性。
总的来说正常流布局主要是使用inline-block来作为内容的容器利用块级格式化上下文的纵向排布和行内级格式化上下文的横向排布来完成布局的我们需要根据需求的横向和纵向排布要求来选择元素的display属性。
## 结语
这次的文章中,我们一起学习了正常流,我们可以用一句话来描述正常流的排版行为,那就是:依次排列,排不下了换行。这也是理解它最简单最源头的方式。
我们将正常流的知识分成了三个部分。
<li>
正常流的行为部分,我们从一些感性认知出发,帮助你从思路和源头上理解正常流的行为。
</li>
<li>
正常流的原理部分我用更严格的描述方式给你讲解了CSS标准中规定的正常流排版逻辑。
</li>
<li>
最后的正常流应用部分,我以两个经典布局问题等分布局和自适应宽为例,为你讲解了正常流实际使用的一些技巧。
</li>
最后留给你一个思考题用JavaScript写一个仅包含inline-block的正常流布局算法。你写好的话可以留言给我我们一起讨论。
# 猜你喜欢
[<img src="https://static001.geekbang.org/resource/image/1a/08/1a49758821bdbdf6f0a8a1dc5bf39f08.jpg" alt="unpreview">](https://time.geekbang.org/course/intro/163?utm_term=zeusMTA7L&amp;utm_source=app&amp;utm_medium=chongxueqianduan&amp;utm_campaign=163-presell)

View File

@@ -0,0 +1,182 @@
<audio id="audio" title="CSS渲染CSS是如何绘制颜色的" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/71/fe/711438b80226d3d7f57ed180285fa1fe.mp3"></audio>
你好我是winter今天我们来学习一下CSS的渲染相关的属性。
我们在布局篇讲到CSS的一些属性决定了盒的位置那么今天我讲到的属性就决定了盒如何被渲染。
按照惯例,还是先从简单得讲起,首先我们来讲讲颜色。
## 颜色的原理
首先我们来讲讲颜色,最常见的颜色相关的属性就是 `color``background-color`
这两个属性没什么好讲的,它们分别表示文字颜色和背景颜色,我们这里重点讲讲颜色值。
### RGB颜色
我们在计算机中最常见的颜色表示法是RGB颜色**它符合光谱三原色理论:红、绿、蓝三种颜色的光可以构成所有的颜色。**
<img src="https://static001.geekbang.org/resource/image/7f/a1/7f5bf39cbe44e36758683a674f9fcfa1.png" alt="">
为什么是这三种颜色呢?这跟人类的视神经系统相关,人类的视觉神经分别有对红、绿、蓝三种颜色敏感的类型。
顺便提一下人类对红色的感觉最为敏感所以危险信号提示一般会选择红色而红绿色盲的人就是红和绿两种神经缺失一种。其它的动物视觉跟人可能不太一样比如皮皮虾拥有16种视锥细胞所以我猜它们看到的世界一定特别精彩。
现代计算机中多用 0 - 255 的数字表示每一种颜色,这正好占据了一个字节,每一个颜色就占据三个字节。
这个数字远远超过了人体的分辨能力因此上世纪90年代刚推出这样的颜色系统的时候它被称作真彩色。早年间还有更节约空间但是精度更低的16色、256色、8位色和16位色表示法。
红绿蓝三种颜色的光混合起来就是白光没有光就是黑暗所以在RGB表示法中三色数值最大表示白色三色数值为0表示黑色。
### CMYK颜色
如果你上过小学美术课,应该听过“红黄蓝”三原色的说法,这好像跟我们说的不太一样。实际上是这样的,颜料显示颜色的原理是它吸收了所有别的颜色的光,只反射一种颜色,所以颜料三原色其实是红、绿、蓝的补色,也就是:品红、黄、青。因为它们跟红、黄、蓝相近,所以有了这样的说法。
<img src="https://static001.geekbang.org/resource/image/15/1b/15fefe9f80ec8e1f7bd9ecd223feb61b.png" alt="">
在印刷行业使用的就是这样的三原色品红、黄、青来调配油墨这种颜色的表示法叫做CMYK它用一个四元组来表示颜色。
你一定会好奇,为什么它比三原色多了一种,其实答案并不复杂,在印刷行业中,黑色颜料价格最低,而品红、黄、青颜料价格较贵,如果要用三原色调配黑色,经济上是不划算的,所以印刷时会单独指定黑色。
对CMYK颜色表示法来说同一种颜色会有多种表示方案但是我们参考印刷行业的习惯会尽量优先使用黑色。
### HSL颜色
好了讲了这么多其实还没有涉及今天的主角HSL颜色。接下来我们就讲一讲。
我们刚才讲的颜色是从人类的视觉原理建模,应该说是十分科学了。但是,人类对颜色的认识却并非来自自己的神经系统,当我们把阳光散射,可以得到七色光:红橙黄绿蓝靛紫,实际上,阳光接近白光,它包含了各种颜色的光,它散射之后,应该是个基本连续的。这说明对人的感知来说,颜色远远大于红、绿、蓝。
因此HSL这样的颜色模型被设计出来了它用一个值来表示人类认知中的颜色我们用专业的术语叫做色相H。加上颜色的纯度S和明度L就构成了一种颜色的表示。
<img src="https://static001.geekbang.org/resource/image/a3/ce/a3016a6ff178870d6dba23f807b0dfce.png" alt="">
在这里我需要特别推荐HSL颜色因为它是一种语义化的颜色。当我们对一张图片改变色相时人们感知到的是“图片的颜色变了”。这里先容我卖个关子具体的例子待我们讲完了渐变再看。
### 其它颜色
接下来我们讲一讲RGBARGBA是代表Red红色、Green绿色、Blue蓝色和Alpha的色彩空间。RGBA颜色被用来表示带透明度的颜色实际上Alpha通道类似一种颜色值的保留字。在CSS中Alpha通道被用于透明度所以我们的颜色表示被称作 RGBA而不是RGBOOpacity
为了方便使用CSS还规定了名称型的颜色它内置了大量140种的颜色名称。不过这里我要挑出两个颜色来讲一讲gold和银silver
如果你使用过这两个颜色你会发现gold和银silver的视觉表现跟我们想象中的金色和银色相差甚远。与其被叫做金色和银色它们看起来更像是难看的暗黄色和浅灰色。
为什么会这样呢?在人类天然的色彩认知中,实际上混杂了很多其它因素,金色和银色不仅仅是一种颜色,它还意味着一定的镜面反光程度,在同样的光照条件下,金属会呈现出更亮的色彩,这并非是用一个色值可以描述的,这就引出了我们接下来要讲的渐变。
## 渐变
在CSS中`background-image`这样的属性可以设为渐变。CSS中支持两种渐变一种是线性渐变一种是放射性渐变我们先了解一下它们的基本用法
线性渐变的写法是:
```
linear-gradient(direction, color-stop1, color-stop2, ...);
```
这里的direction可以是方向也可以是具体的角度。例如
- to bottom
- to top
- to left
- to right
- to bottom left
- to bottom right
- to top left
- to top right
- 120deg
- 3.14rad
以上这些都是合理的方向取值。
color-stop是一个颜色和一个区段例如
- rgba(255,0,0,0)
- orange
- yellow 10%
- green 20%
- lime 28px
我们组合一下,产生一个“真正的金色”的背景:
```
&lt;style&gt;
#grad1 {
height: 200px;
background: linear-gradient(45deg, gold 10%, yellow 50%, gold 90%);
}
&lt;/style&gt;
&lt;div id="grad1"&gt;&lt;/div&gt;
```
放射性渐变需要一个中心点和若干个颜色:
```
radial-gradient(shape size at position, start-color, ..., last-color);
```
当我们应用的每一种颜色都是HSL颜色时就产生了一些非常有趣的效果比如我们可以通过变量来调整一个按钮的风格
```
&lt;style&gt;
.button {
display: inline-block;
outline: none;
cursor: pointer;
text-align: center;
text-decoration: none;
font: 14px/100% Arial, Helvetica, sans-serif;
padding: .5em 2em .55em;
text-shadow: 0 1px 1px rgba(0,0,0,.3);
border-radius: .5em;
box-shadow: 0 1px 2px rgba(0,0,0,.2);
color: white;
border: solid 1px ;
}
&lt;/style&gt;
&lt;div class="button orange"&gt;123&lt;/div&gt;
```
```
var btn = document.querySelector(".button");
var h = 25;
setInterval(function(){
h ++;
h = h % 360;
btn.style.borderColor=`hsl(${h}, 95%, 45%)`
btn.style.background=`linear-gradient(to bottom, hsl(${h},95%,54.1%), hsl(${h},95%,84.1%))`
},100);
```
## 形状
CSS中的很多属性还会产生形状比如我们常见的属性
- border
- box-shadow
- border-radius
这些产生形状的属性非常有趣我们也能看到很多利用它们来产生的CSS黑魔法。然而这里我有一个相反的建议我们仅仅把它们用于基本的用途把border用于边框、把阴影用于阴影把圆角用于圆角所有其它的场景都有一个更好的替代品datauri+svg。
## 总结
今天我们介绍了CSS中渲染相关的属性颜色和形状。
我们重点介绍了CSS的颜色系统从颜色基本原理讲解了RGB颜色、CMYK颜色和HSV颜色我们还讲解了Alpha通道。
接下来我们又讲了颜色的一个重要应用:渐变,我们可以把渐变看作是一个更复杂的颜色,它非常实用,能够用渐变绘制很多的图像。
最后我们讲解了形状相关的属性以及SVG应用的一个小技巧。
### 思考题
<img src="https://static001.geekbang.org/resource/image/0f/ac/0f6f4cc6d564df9986e0108cb8a427ac.jpg" alt="">
折衷鹦鹉是一种可爱的鸟类但是雄性折衷鹦鹉居然是跟雌性颜色不一样你能用JavaScript和canvas把这只雄性折衷鹦鹉变成跟雌性一样可爱的红色吗

View File

@@ -0,0 +1,300 @@
<audio id="audio" title="CSS语法除了属性和选择器你还需要知道这些带@的规则" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b9/41/b903afd711372b39af059389a5db9141.mp3"></audio>
你好我是winter。
今天我们进入CSS的学习。CSS是前端工程师几乎每天都要用的技术了不过CSS的学习资料却是最糟糕的这是因为CSS并没有像HTML和JavaScript那样的一份标准文档。
如果我们到W3C的网站上搜索看看可以得到一些信息
- [https://www.w3.org/TR/?title=css](https://www.w3.org/TR/?title=css)
在这里我们一共看到了98份CSS相关的标准它们各自从一些角度规定了CSS的特性。
这里我们暂且去掉Working Draft状态的标准可以得到22份候选标准和6份推荐标准。
既然我们的专栏内容强调去系统性学习CSS于是面对这22+6份标准我们就又需要一条线索才能把这些离散的标准组织成易于理解和记忆的形式。
在这样的需求下我找到的线索就是CSS语法任何CSS的特性都必须通过一定的语法结构表达出来所以语法可以帮助我们发现大多数CSS特性。
CSS语法的最新标准你可以戳这里查看
- [https://www.w3.org/TR/css-syntax-3/](https://www.w3.org/TR/css-syntax-3/)
这篇文档的阅读体验其实是非常糟糕的它对CSS语法的描述使用了类似LL语法分析的伪代码而且没有描述任何具体的规则。
这里你就不必自己去阅读了,我来把其中一些有用的关键信息抽取出来描述一下,我们一起来看看。
我们拿到这份标准可以看到去除空格、HTML注释等无效信息**CSS的顶层样式表由两种规则组成的规则列表构成一种被称为 at-rule也就是at 规则,另一种是 qualified rule也就是普通规则。**
at-rule由一个 @ 关键字和后续的一个区块组成如果没有区块则以分号结束。这些at-rule在开发中使用机会远远小于普通的规则所以它的大部分内容你可能会感觉很陌生。
这些at规则正是掌握CSS的一些高级特性所必须的内容。qualified rule则是指普通的CSS规则也就是我们所熟识的由选择器和属性指定构成的规则。
## at 规则
好了现在我们已经知道了CSS语法的整体结构接下来我们要做的是一个体力活从所有的CSS标准里找到所有可能的 at-rule不用谢我已经帮你找好了如果页面定位不准你可以打开页面搜索关键字
- @charset [https://www.w3.org/TR/css-syntax-3/](https://www.w3.org/TR/css-syntax-3/)
- @import [https://www.w3.org/TR/css-cascade-4/](https://www.w3.org/TR/css-cascade-4/)
- @media [https://www.w3.org/TR/css3-conditional/](https://www.w3.org/TR/css3-conditional/)
- @page [https://www.w3.org/TR/css-page-3/](https://www.w3.org/TR/css-page-3/)
- @counter-style [https://www.w3.org/TR/css-counter-styles-3](https://www.w3.org/TR/css-counter-styles-3)
- @keyframes [https://www.w3.org/TR/css-animations-1/](https://www.w3.org/TR/css-animations-1/)
- @fontface [https://www.w3.org/TR/css-fonts-3/](https://www.w3.org/TR/css-fonts-3/)
- @supports [https://www.w3.org/TR/css3-conditional/](https://www.w3.org/TR/css3-conditional/)
- @namespace [https://www.w3.org/TR/css-namespaces-3/](https://www.w3.org/TR/css-namespaces-3/)
这里的每一种@规则背后都是一组CSS的知识。在我们的课程中有些会重点介绍不过为了先给你建立起一个整体的认知我们这里会给所有的@规则提供一些简单的例子和介绍
### @charset
@charset用于提示CSS文件使用的字符编码方式,它如果被使用,必须出现在最前面。这个规则只在给出语法解析阶段前使用,并不影响页面上的展示效果。
```
@charset "utf-8";
```
### @import
@import用于引入一个CSS文件,除了@charset规则不会被引入@import可以引入另一个文件的全部内容
```
@import "mystyle.css";
@import url("mystyle.css");
```
```
@import [ &lt;url&gt; | &lt;string&gt; ]
[ supports( [ &lt;supports-condition&gt; | &lt;declaration&gt; ] ) ]?
&lt;media-query-list&gt;? ;
```
通过代码我们可以看出import还支持 supports 和media query形式。
### @media
media就是大名鼎鼎的media query使用的规则了它能够对设备的类型进行一些判断。在media的区块内是普通规则列表。
```
@media print {
body { font-size: 10pt }
}
```
### @page
page用于分页媒体访问网页时的表现设置页面是一种特殊的盒模型结构除了页面本身还可以设置它周围的盒。
```
@page {
size: 8.5in 11in;
margin: 10%;
@top-left {
content: "Hamlet";
}
@top-right {
content: "Page " counter(page);
}
}
```
### @ counter-style
counter-style产生一种数据用于定义列表项的表现。
```
@counter-style triangle {
system: cyclic;
symbols: ‣;
suffix: " ";
}
```
### @ key-frames
keyframes产生一种数据用于定义动画关键帧。
```
@keyframes diagonal-slide {
from {
left: 0;
top: 0;
}
to {
left: 100px;
top: 100px;
}
}
```
### @ fontface
fontface用于定义一种字体icon font技术就是利用这个特性来实现的。
```
@font-face {
font-family: Gentium;
src: url(http://example.com/fonts/Gentium.woff);
}
p { font-family: Gentium, serif; }
```
### @ support
support检查环境的特性它与media比较类似。
### @ namespace
用于跟XML命名空间配合的一个规则表示内部的CSS选择器全都带上特定命名空间。
### @ viewport
用于设置视口的一些特性不过兼容性目前不是很好多数时候被HTML的meta代替。
### 其它
除了以上这些还有些目前不太推荐使用的at规则。
- @color-profile 是 SVG1.0 引入的CSS特性但是实现状况不怎么好。
- @document 还没讨论清楚被推迟到了CSS4中。
- @font-feature-values 。
## 普通规则
接下来我们进入qualified rule也就是普通规则的部分看看这里有什么需要我们记住的内容。
qualified rule主要是由选择器和声明区块构成。声明区块又由属性和值构成。我在下面的列表中介绍了这部分语法的组成要点。
<li>普通规则
<ul>
- 选择器
<li>声明列表
<ul>
- 属性
<li>
<ul>
- 值的类型
- 函数
### 选择器
我们先来看看选择器,它有一份独立的标准,我们可以参考这个网址:
[https://www.w3.org/TR/selectors-4/](https://www.w3.org/TR/selectors-4/)
这份标准不在我们前面的过滤条件中它属于CSS和HTML共用的标准。
关于选择器的叠加规则等知识我们后文会专门的一节课程来讲,这里我们就从语法的角度介绍一下选择器。
在选择器标准的最后,附有一张选择器的语法表,从这份语法表,我们可以理清楚记忆选择器的思路。
我们从语法结构可以看出,任何选择器,都是由几个符号结构连接的:空格、大于号、加号、波浪线、双竖线,这里需要注意一下,空格,即为后代选择器的优先级较低。
然后对每一个选择器来说如果它不是伪元素的话由几个可选的部分组成标签类型选择器id、class、属性和伪类它们中只要出现一个就构成了选择器。
如果它是伪元素,则在这个结构之后追加伪元素。只有伪类可以出现在伪元素之后。我在下面用一个列表(不太严谨地)整理了选择器的语法结构:<br>
<img src="https://static001.geekbang.org/resource/image/4f/67/4fa32e5cf47c72a58f7a8211d4e8fc67.png" alt="">
我们在这里可以参考一个示例图:
<img src="https://static001.geekbang.org/resource/image/8b/7c/8bdd0a249ab1dbf8b854b2decd7eb87c.png" alt="">
(语法结构分析示例)<br>
看完了选择器,我们继续来看看声明部分的语法。
## 声明:属性和值
声明部分是一个由“属性:值”组成的序列。
**属性**是由中划线、下划线、字母等组成的标识符CSS还支持使用反斜杠转义。我们需要注意的是属性不允许使用连续的两个中划线开头这样的属性会被认为是CSS变量。
在[CSS Variables标准](https://www.w3.org/TR/css-variables/)中,以双中划线开头的属性被当作变量,与之配合的则是 var 函数:
```
:root {
--main-color: #06c;
--accent-color: #006;
}
/* The rest of the CSS file */
#foo h1 {
color: var(--main-color);
}
```
**值**的部分,主要[在标准 CSS Values and Unit](https://www.w3.org/TR/css-values-4/)根据每个CSS属性可以取到不同的值这里的值可能是字符串、标识符。
CSS属性值可能是以下类型。
- CSS范围的关键字initialunsetinherit任何属性都可以的关键字。
- 字符串比如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接受属性值的控制。
## 总结
在这一部分我们介绍了CSS语法的总体结构CSS的语法总体结构是由两种规则列表构成一种是at 规则,另一种是普通规则。
在at规则中我举了13个以上的例子并逐个进行了简单的介绍。而在普通规则的部分我介绍了选择器和声明区块是普通规则的主要组成部分。
并且,我给出了一个(不太严谨)的选择器语法结构,声明区块则由属性和值构成,这一部分我们重点介绍了函数。
从整体上去掌握内容再去定位到单个细节这对于我们学习CSS有非常重要的提示作用。
最后给你留一个思考问题CSS的函数有很多本文也提到了不少请你也一起查阅资料试着总结一下你能找到多少种CSS函数

View File

@@ -0,0 +1,372 @@
<audio id="audio" title="CSS选择器伪元素是怎么回事儿" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b4/b6/b463eb2fdc9a5408fb9c6b46694724b6.mp3"></audio>
你好我是winter。
在上一篇文章中,我已经给你介绍了一些简单选择器,这一节课我会继续给你介绍选择器的几个机制:选择器的组合、选择器的优先级和伪元素。
## 选择器的组合
在CSS规则中选择器部分是一个选择器列表。
选择器列表是用逗号分隔的复杂选择器序列;复杂选择器则是用空格、大于号、波浪线等符号连接的复合选择器;复合选择器则是连写的简单选择器组合。
根据选择器列表的语法,选择器的连接方式可以理解为像四则运算一样有优先级。
<li>
第一优先级
<ul>
- 无连接符号
第二优先级
- “空格”
- “~”
- “+”
-&gt;
- “||”
第三优先级
- “,”
例如以下选择器:
```
.c,.a&gt;.b.d {
/*......*/
}
```
我们应该理解为这样的结构。
<li>.c,.a&gt;.b.d
<ul>
- .c
<li>.a&gt;.b.d
<ul>
- .a
<li>.b.d
<ul>
- .b
- .d
复合选择器表示简单选择器中“且”的关系,例如,例子中的“ .b.d ”表示选中的元素必须同时具有b和d两个class。
复杂选择器是针对节点关系的选择,它规定了五种连接符号。
- **“空格”**:后代,表示选中所有符合条件的后代节点, 例如“ .a .b ”表示选中所有具有class为a的后代节点中class为b的节点。
- **“&gt;”** :子代,表示选中符合条件的子节点,例如“ .a&gt;.b ”表示选中所有“具有class为a的子节点中class为b的节点”。
- **“~” **: 后继,表示选中所有符合条件的后继节点,后继节点即跟当前节点具有同一个父元素,并出现在它之后的节点,例如“ .a~.b ”表示选中所有具有class为a的后继中class为b的节点。
- **“+”**直接后继表示选中符合条件的直接后继节点直接后继节点即nextSlibling。例如 “.a+.b ”表示选中所有具有class为a的下一个class为b的节点。
- **“||”**:列选择器,表示选中对应列中符合条件的单元格。
我们在实际使用时,比较常用的连接方式是“空格”和“&gt;”。
工程实践中一般会采用设置合理的class的方式来避免过于复杂的选择器结构这样更有利于维护和性能。
空格和子代选择器通常用于组件化场景当组件是独立开发时很难完全避免class重名的情况如果为组件的最外层容器元素设置一个特别的class名生成CSS规则时则全部使用后代或者子代选择器这样可以有效避免CSS规则的命名污染问题。
逗号表示“或”的关系实际上可以把它理解为“两条内容一样的CSS规则”的一种简写。如我们开头的例子可以理解成与下面的代码等效
```
.c {
/*......*/
}
.a&gt;.b.d {
/*......*/
}
```
到这里,我们就讲完了如何用简单选择器组合成复合选择器和复杂选择器,形成选择器列表,这能够帮助我们应对各种复杂的需求。
CSS选择器是基于规则生效的同一个元素命中多条规则是非常常见的事情。不同规则指定同一个属性为不同值时就需要一个机制来解决冲突。这个机制就是接下来我们要讲的选择器优先级。
## 选择器的优先级
CSS标准用一个三元组 (a, b, c) 来构成一个复杂选择器的优先级。
- id选择器的数目记为a
- 伪类选择器和class选择器的数目记为b
- 伪元素选择器和标签选择器数目记为c
- “*” 不影响优先级。
CSS标准建议用一个足够大的进制获取“ a-b-c ”来表示选择器优先级。
即:
```
specificity = base * base * a + base * b + c
```
其中base是一个“足够大”的正整数。关于base历史中有些趣闻早年IE6采用256进制于是就产生“256个class优先级等于一个id”这样的奇葩问题后来扩大到65536基本避免了类似的问题。
现代浏览器多采用了更大的数量我们正常编写的CSS规则数量不太可能达到数万因此我们可以认为这样的base就足够大了。
行内属性的优先级永远高于CSS规则浏览器提供了一个“口子”就是在选择器前加上“!import”。
这个用法非常危险,因为它相当于一个新的优先级,而且此优先级会高于行内属性。
同一优先级的选择器遵循“后面的覆盖前面的”原则,我们可以看一个例子:
```
&lt;div id="my" class="x y"&gt;text&lt;div&gt;
```
```
.x {
background-color:lightblue;
}
.y {
background-color:lightgreen;
}
```
调换“.x”和“.y”我们可以得到不同的显示效果。选择器的优先级是针对单条规则的多条规则的选择器同时命中元素优先级不会发生叠加。
```
&lt;div id="my" class="x y z"&gt;text&lt;div&gt;
```
```
.x {
background-color:lightblue;
}
.z {
background-color:lightblue;
}
.y {
background-color:lightgreen;
}
```
在这个例子中,“.x ”和“.z ”都指定了背景色为浅蓝色,但是因为“.y ”规则在最后,所以最终显示结果为浅绿色。另外一个需要注意的是,选择器的优先级是针对复杂选择器的优先级,选择器列表不会合并计算优先级。
我们看一个例子:
```
&lt;div id=&quot;my&quot; class=&quot;x y z&quot;&gt;text&lt;div&gt;
```
```
.x, .z {
background-color:lightblue;
}
.y {
background-color:lightgreen;
}
```
这里选择器列表“ .x, .z”命中了div但是它的两项分别计算优先级所以最终优先级仍跟“ .y” 规则相同。
以上就是选择器优先级的相关规则了,虽然我们这里介绍了详细的计算方式,但是我认为选择器的使用上,如果产生复杂的优先级计算,代码的可读性一定是有问题的。
所以实践中,建议你“根据 id 选单个元素”“class和class的组合选成组元素”“tag选择器确定页面风格”这样的简单原则来使用选择器不要搞出过于复杂的选择器。
## 伪元素
在上一课,我们有意忽略了一种重要的简单选择器:伪元素。
我之所以没有把它放在简单选择器中,是因为伪元素本身不单单是一种选择规则,它还是一种机制。
所以本节课,我就来讲一讲伪元素机制。伪元素的语法跟伪类相似,但是实际产生的效果却是把不存在的元素硬选出来。
目前兼容性达到可用的伪元素有以下几种。
- ::first-line
- ::first-letter
- ::before
- ::after
下面我们就来分别讲讲它们。
**::first-line 和 ::first-letter 是比较类似的伪元素**,其中一个表示元素的第一行,一个表示元素的第一个字母。
我们可以看一个示例:
```
&lt;p&gt;This is a somewhat long HTML
paragraph that will be broken into several
lines. The first line will be identified
by a fictional tag sequence. The other lines
will be treated as ordinary lines in the
paragraph.&lt;/p&gt;
```
```
p::first-line {
text-transform: uppercase
}
```
这一段代码把段落的第一行字母变为大写。注意这里的第一行指的是排版后显示的第一行跟HTML代码中的换行无关。
::first-letter 则指第一个字母。首字母变大并向左浮动是一个非常常见的排版方式。
```
&lt;p&gt;This is a somewhat long HTML
paragraph that will be broken into several
lines. The first line will be identified
by a fictional tag sequence. The other lines
will be treated as ordinary lines in the
paragraph.&lt;/p&gt;
```
```
p::first-letter {
text-transform: uppercase;
font-size:2em;
float:left;
}
```
虽然听上去很简单但是实际上我们遇到的HTML结构要更为复杂一旦元素中不是纯文本规则就变得复杂了。
CSS标准规定了first-line必须出现在最内层的块级元素之内。因此我们考虑以下代码。
```
&lt;div&gt;
&lt;p id=a&gt;First paragraph&lt;/p&gt;
&lt;p&gt;Second paragraph&lt;/p&gt;
&lt;/div&gt;
```
```
div&gt;p#a {
color:green;
}
div::first-line {
color:blue;
}
```
这段代码最终结果第一行是蓝色因为p是块级元素所以伪元素出现在块级元素之内所以内层的color覆盖了外层的color属性。
如果我们把p换成span结果就是相反的。
```
&lt;div&gt;
&lt;span id=a&gt;First paragraph&lt;/span&gt;&lt;br/&gt;
&lt;span&gt;Second paragraph&lt;/span&gt;
&lt;/div&gt;
```
```
div&gt;span#a {
color:green;
}
div::first-line {
color:blue;
}
```
这段代码的最终结果是绿色这说明伪元素在span之外。
::first-letter的行为又有所不同它的位置在所有标签之内我们把前面的代码换成::first-letter。
```
&lt;div&gt;
&lt;span id=a&gt;First paragraph&lt;/span&gt;&lt;br/&gt;
&lt;span&gt;Second paragraph&lt;/span&gt;
&lt;/div&gt;
```
```
div&gt;span#a {
color:green;
}
div::first-letter {
color:blue;
}
```
执行这段代码我们可以看到首字母变成了蓝色这说明伪元素出现在span之内。
CSS标准只要求 ::first-line 和 ::first-letter 实现有限的几个CSS属性都是文本相关这些属性是下面这些。
<img src="https://static001.geekbang.org/resource/image/6e/48/6e050ee9f7a0b1657388271cceb0c548.png" alt="">
**接下来我们说说 ::before 和 ::after 伪元素。**
这两个伪元素跟前面两个不同的是,它不是把已有的内容套上一个元素,而是真正的无中生有,造出一个元素。
::before 表示在元素内容之前插入一个虚拟的元素,::after 则表示在元素内容之后插入。
这两个伪元素所在的CSS规则必须指定content属性才会生效我们看下例子
```
&lt;p class=&quot;special&quot;&gt;I'm real element&lt;/p&gt;
```
```
p.special::before {
display: block;
content: &quot;pseudo! &quot;;
}
```
这里要注意一点,::before 和 ::after 还支持content为counter
```
&lt;p class=&quot;special&quot;&gt;I'm real element&lt;/p&gt;
p.special::before {
display: block;
content: counter(chapno, upper-roman) &quot;. &quot;;
}
```
这对于实现一些列表样式是非常有用的。
::before 和 ::after 中支持所有的CSS属性。实际开发中这两个伪元素非常有用有了这两个伪元素一些修饰性元素可以使用纯粹的CSS代码添加进去这能够很好地保持HTML代码中的语义既完成了显示效果又不会让DOM中出现很多无语义的空元素。
## 结语
这一课我们讲了CSS选择器的三种机制选择器的组合、选择器优先级、以及伪元素。
在选择器组合这一部分,我们讲到了,选择器的连接方式像四则运算一样有优先级。
第一优先级是无连接符号;第二优先级是:“空格”“~”“+”“&gt;”“||”;第三优先级是“,”。
然后我们又介绍了选择器优先级的计算方式。
最后我们介绍了伪元素,我们逐次讲解了:
- ::first-line
- ::first-letter
- ::before
- ::after
四种伪元素。伪元素的语法跟伪类相似,但是实际产生的效果是把不存在的元素硬选出来。这一点就与伪类不太一样了。
结合上一节课我们讲的简单选择器对它们灵活运用就能够满足大部分CSS的使用场景的需求了。
最后留给你一个问题你所在的团队如何规定CSS选择器的编写规范你觉得它好吗
# 猜你喜欢
[<img src="https://static001.geekbang.org/resource/image/1a/08/1a49758821bdbdf6f0a8a1dc5bf39f08.jpg" alt="unpreview">](https://time.geekbang.org/course/intro/163?utm_term=zeusMTA7L&amp;utm_source=app&amp;utm_medium=chongxueqianduan&amp;utm_campaign=163-presell)

View File

@@ -0,0 +1,129 @@
<audio id="audio" title="HTML·ARIA可访问性是只给盲人用的特性么" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9d/19/9d1cc0d86b852a5088bf6e47008f5319.mp3"></audio>
你好我是winter。
我们都知道HTML已经是一个完整的语义系统。在前面的课程中我们围绕着HTML本身做了讲解但是在实际应用中我们还会用到一些它的扩展。今天我们要讲的ARIA就是其中重要的一部分。
ARIA全称为Accessible Rich Internet Applications它表现为一组属性是用于可访问性的一份标准。关于可访问性它被提到最多的就是它可以为视觉障碍用户服务但是这是一个误解。
实际上,可访问性其实是一个相当大的课题,它的定义包含了各种设备访问、各种环境、各种人群访问的友好性。不单单是永久性的残障人士需要用到可访问性,健康的人也可能在特定时刻处于需要可访问性的环境。
我们今天讲的ARIA是以交互形式来标注各种元素的一类属性所以在ARIA属性中你可以看到很多熟悉的面孔交互形式往往跟我们直觉中的“控件”非常相似。
所以我们的课程特意把ARIA加入还有一个原因ARIA的角色对于我们UI系统的设计有重要的参考意义。
## 综述
我们先整体来看看ARIA给HTML元素添加的一个核心属性就是role我们来看一个例子
```
&lt;span role="checkbox" aria-checked="false" tabindex="0" aria-labelledby="chk1-label"&gt;
&lt;/span&gt; &lt;label id="chk1-label"&gt;Remember my preferences&lt;/label&gt;
```
这里我们给一个span添加了checkbox角色这样表示我们这个span被用于checkbox这意味着我们可能已经用JS代码绑定了这个span的click事件并且以checkbox的交互方式来处理用户操作。
同时ARIA系统还提供了一系列ARIA属性给checkbox这个role这意味着**我们可以通过HTML属性变化来理解这个JavaScript组件的状态**读屏软件等三方客户端就可以理解我们的UI变化这正是ARIA标准的意义。
role的定义是一个树形的继承关系我们先来理解一下它的整体结构
<img src="https://static001.geekbang.org/resource/image/ae/69/aeccf64871b309735054912fbbb18a69.jpg" alt="">
其中widget表示一些可交互的组件structure表示文档中的结构window则代表窗体。
接下来让我们分类了解一下重要的ARIA角色。
## Widget角色
我们刚刚已经讲过一个widget role就是checkbox。
这一类角色跟我们桌面开发中的控件类似,它表示一个可交互的组件,它们有:
<img src="https://static001.geekbang.org/resource/image/10/dd/10ea9eb62d60fb4bfb18c27da50836dd.jpg" alt="">
我们这里按照继承关系给出一份列表和简要说明:
<img src="https://static001.geekbang.org/resource/image/03/f1/038e1152c9bddc7ed864d271691d17f1.jpeg" alt="">
ARIA role允许多继承这里有些角色我没有重复写。
注意这些role可以出现在任何一个HTML元素之上同时要注意这些ARIA属性不会真实地改变任何一个元素的行为比如我们刚才讲的checkbox即使我们给一个span添加了Checkbox角色我们也需要用JavaScript编写相应的逻辑。
这些widget同时还会带来对应的ARIA属性比如我们的Checkbox角色会带来两个属性
- aria-checked 表示复选框是否已经被选中;
- aria-labelledby 表示复选框对应的文字。
而Button角色则会带来另外两个属性
- aria-pressed 按钮是否已经被按下;
- aria-expanded 按钮控制的目标是否已经被展开。
除了它们本身的属性之外可交互组件还有继承来的属性比如switch角色继承了checkbox因此它也可以使用aria-checked属性。
在WAI-ARIA标准中你可以找到所有的角色和对应的属性我们这里就不一一列举了。
- [https://www.w3.org/TR/wai-aria/](https://www.w3.org/TR/wai-aria/)
很多这些ARIA属性都是需要在JavaScript中维护的。
如果我们要实现一份组件库这些widget role和它们对应的aria属性是非常好的参考。
如果你是组件的实现者也希望你在实现组件时把对应的ARIA属性自动维护好。
除了简单的widget还有一些比较复杂的角色需要多个角色一起配合。我们来逐个了解一下。
Combobox 是一个带选项的输入框,我们常见的搜索引擎,一般都会提供这样的输入框,当输入时,它会提供若干提示选项。
Grid 是一个表格,它会分成行、列,行列又有行头和列头表示行、列的意义。
Tablist 是一个可切换的结构一般被称为选项卡它包含了tab头和tabpanel在tab容器中可能包含各种组件。
Listbox 是一个可选中的列表它内部具有角色为Option的选项。
Menu 是指菜单菜单中可以加入嵌套的菜单项Menuitem角色除了普通菜单项还可以有Menuitemcheckbox 带复选框的菜单栏和Menuitemradio 带单选框的菜单栏。
Radiogroup 是一组互斥的单选框的容器它的内部可以由若干个角色为radio的单选框。
Tree 是树形控件,它的内部含有 Treeitem 树形控件项它还有一种升级形式是Treegrid。
## structure角色
结构角色其实跟HTML5中不少新标签作用重合了这里建议优先使用HTML5标签。
这部分角色的作用类似于语义化标签,但是内容稍微有些不同,我们这里就不详细讲解了,仅仅给出一张图供你参考:
<img src="https://static001.geekbang.org/resource/image/b2/7a/b21a82fd68a885f751123f48a7e26b7a.jpg" alt="">
separator在允许焦点时属于组件在不允许焦点时属于文档结构。
这里我们需要特别提出Landmark角色这个概念Landmark角色直接翻译是地标它是ARIA标准中总结的Web网页中最常见的8个结构Landmark角色实际上是section的子类这些角色在生成页面摘要时有很大可能性需要被保留它们是
<img src="https://static001.geekbang.org/resource/image/9a/75/9aee7029d4bf684a8679a6776d6e9075.jpg" alt="">
## window角色
在我们的网页中有些元素表示“新窗口”这时候会用到window角色。window系角色非常少只有三个角色
<li>window
<ul>
<li>dialog
<ul>
- alertdialog
dialog可能会产生“焦点陷阱”也就是说当这样的角色被激活时焦点无法离开这个区域。
## 总结
今天我介绍了ARIA相关的知识我们分几个部分学习了如何使用ARIA属性来提高页面的可访问性。
我们以ARIA角色为中心讲解了ARIA定义的语义体系。我们可以把ARIA分为三类。
- Widget角色主要是各种可交互的控件。
- 结构角色:文档的结构。
- 窗体角色:弹出的窗体。
今天的课后小问题是请找一个支持图结构可视化的JS库把所有ARIA的继承关系用可视化的方式展现出来。

View File

@@ -0,0 +1,171 @@
<audio id="audio" title="HTML元信息类标签你知道head里一共能写哪几种标签吗" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/7f/45/7f3fb5790c70407f8bef3829b6b53645.mp3"></audio>
你好我是winter。
我们在前面的HTML部分的课程中已经学习了语义标签。这些标签涵盖了我们日常开发用到的多数标签也是我们编写代码时最常用的一批标签。
但是我们今天要讲的标签,重要性丝毫不弱于语义类标签,这就是页面元信息类标签。
我们可以先来了解一下什么是元信息类标签。所谓元信息是指描述自身的信息元信息类标签就是HTML用于描述文档自身的一类标签它们通常出现在head标签中一般都不会在页面被显示出来与此相对其它标签如语义类标签描述的是业务
元信息多数情况下是给浏览器、搜索引擎等机器阅读的,有时候这些信息会在页面之外显示给用户,有时候则不会。
元信息类标签数量不多,我在这里就逐一为你介绍一下。
## head标签
首先我们先来了解一下head标签head标签本身并不携带任何信息它主要是作为盛放其它语义类标签的容器使用。
head标签规定了自身必须是html标签中的第一个标签它的内容必须包含一个title并且最多只能包含一个base。如果文档作为iframe或者有其他方式指定了文档标题时可以允许不包含title标签。
## title标签
title标签表示文档的标题从字面上就非常容易理解。这里我就讲讲需要注意的地方。
你还记得吗我们的语义类标签中也有一组表示标题的标签h1-h6。
heading 和 title 两个英文单词意义区分十分微妙,在中文中更是找不到对应的词汇来区分。但是实际使用中,两者确实有一定区别。
在HTML标准中特意讨论了这个问题。我们思考一下假设有一个介绍蜜蜂跳舞求偶仪式的科普页面我们试着把以下两个文字分别对应到title和h1。
- 蜜蜂求偶仪式舞蹈
- 舞蹈
在听/看正确答案前,你不妨先想想,自己的答案是什么呢?为什么?
好了思考之后我们来看看正确答案。正确答案是“蜜蜂求偶仪式舞蹈”放入title“舞蹈”放入h1。
我来讲一讲为什么要这样放呢这主要是考虑到title作为元信息可能会被用在浏览器收藏夹、微信推送卡片、微博等各种场景这时侯往往是上下文缺失的所以title应该是完整地概括整个网页内容的。
而h1则仅仅用于页面展示它可以默认具有上下文并且有链接辅助所以可以简写即便无法概括全文也不会有很大的影响。
## base标签
base标签实际上是个历史遗留标签。它的作用是给页面上所有的URL相对地址提供一个基础。
base标签最多只有一个它改变全局的链接地址它是一个非常危险的标签容易造成跟JavaScript的配合问题所以在实际开发中我比较建议你使用JavaScript来代替base标签。
## meta标签
meta标签是一组键值对它是一种通用的元信息表示标签。
在head中可以出现任意多个meta标签。一般的meta标签由name和content两个属性来定义。name表示元信息的名content则用于表示元信息的值。
它基本用法是下面这样的,你也可以自己动手尝试一下:
```
&lt;meta name=application-name content=&quot;lsForums&quot;&gt;
```
这个标签表示页面所在的web-application名为IsForums。
这里的name是一种比较自由的约定HTTP标准规定了一些name作为大家使用的共识也鼓励大家发明自己的name来使用。
除了基本用法meta标签还有一些变体主要用于简化书写方式或者声明自动化行为。下面我就挑几种重点的内容来分别讲解一下。
### 具有charset属性的meta
从HTML5开始为了简化写法meta标签新增了charset属性。添加了charset属性的meta标签无需再有name和content。
```
&lt;meta charset=&quot;UTF-8&quot; &gt;
```
charset型meta标签非常关键它描述了HTML文档自身的编码形式。因此我建议这个标签放在head的第一个。
```
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
……
```
这样浏览器读到这个标签之前处理的所有字符都是ASCII字符众所周知ASCII字符是UTF-8和绝大多数字符编码的子集所以在读到meta之前浏览器把文档理解多数编码格式都不会出错这样可以最大限度地保证不出现乱码。
一般情况下HTTP服务端会通过http头来指定正确的编码方式但是有些特殊的情况如使用file协议打开一个HTML文件则没有http头这种时候charset meta就非常重要了。
## 具有http-equiv属性的meta
具有http-equiv属性的meta标签表示执行一个命令这样的meta标签可以不需要name属性了。
例如下面一段代码相当于添加了content-type这个http头并且指定了http编码方式。
```
&lt;meta http-equiv=&quot;content-type&quot; content=&quot;text/html; charset=UTF-8&quot;&gt;
```
除了content-type还有以下几种命令
- content-language 指定内容的语言;
- default-style 指定默认样式表;
- refresh 刷新;
- set-cookie 模拟http头set-cookie设置cookie
- x-ua-compatible 模拟http头x-ua-compatible声明ua兼容性
- content-security-policy 模拟http头content-security-policy声明内容安全策略。
### name为viewport的meta
实际上meta标签可以被自由定义只要写入和读取的双方约定好name和content的格式就可以了。
我们来介绍一个meta类型它没有在HTML标准中定义却是移动端开发的事实标准它就是name为viewport的meta。
这类meta的name属性为viewport它的content是一个复杂结构是用逗号分隔的键值对键值对的格式是key=value。
例如:
```
&lt;meta name=&quot;viewport&quot; content=&quot;width=500, initial-scale=1&quot;&gt;
```
这里只指定了两个属性宽度和缩放实际上viewport能控制的更多它能表示的全部属性如下
- width页面宽度可以取值具体的数字也可以是device-width表示跟设备宽度相等。
- height页面高度可以取值具体的数字也可以是device-height表示跟设备高度相等。
- initial-scale初始缩放比例。
- minimum-scale最小缩放比例。
- maximum-scale最大缩放比例。
- user-scalable是否允许用户缩放。
对于已经做好了移动端适配的网页应该把用户缩放功能禁止掉宽度设为设备宽度一个标准的meta如下
```
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no&quot;&gt;
```
## 其它预定义的meta
在HTML标准中还定义了一批meta标签的name可以视为一种有约定的meta我在这里列出来你可以简单了解一下。
application-name如果页面是Web application用这个标签表示应用名称。
- author: 页面作者。
- description页面描述这个属性可能被用于搜索引擎或者其它场合。
- generator: 生成页面所使用的工具主要用于可视化编辑器如果是手写HTML的网页不需要加这个meta。
- keywords: 页面关键字对于SEO场景非常关键。
- referrer: 跳转策略,是一种安全考量。
- theme-color: 页面风格颜色实际并不会影响页面但是浏览器可能据此调整页面之外的UI如窗口边框或者tab的颜色
## 结语
在本课,我们又学习了一批标签,它们是文档用于描述自身的元信息类标签。一些元信息标签可以产生实际的行为,掌握它们对于我们编写代码是必须的。
另一些元信息仅仅是对页面的描述,掌握它们可以使我们编写的页面跟各种浏览器、搜索引擎等结合地更好。
主要包括下面这些内容。
- head元信息的容器。
- title文档标题。
- base页面的基准URL。
- meta: 元信息通用标签。
我们还展开介绍了几种重要的meta标签charset表示页面编码http-equiv表示命令还介绍了一些有约定的meta名称。
最后给你留一个问题你还见过哪些meta标签的用法欢迎留言告诉我。

View File

@@ -0,0 +1,364 @@
<audio id="audio" title="HTML小实验用代码分析HTML标准" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a7/3f/a7864d32419da6d7e6d15a2e9f6a333f.mp3"></audio>
你好我是winter。
前面的课程中我们已经讲解了大部分的HTML标签。
然而为了突出重点我们还是会忽略一些标签类型。比如表单类标签和表格类标签我认为只有少数前端工程师用过比如我在整个手机淘宝的工作生涯中一次表格类标签都没有用到表单类则只用过input也只有几次。
那么剩下的标签我们怎么样去了解它们呢当然是查阅HTML标准。
由于阅读标准有一定门槛需要了解一些机制这节课我为你设计了一个小实验用JavaScript代码去抽取标准中我们需要的信息。
## HTML标准
我们采用WHATWG的living standard标准我们先来看看标准是如何描述一个标签的这里我们看到有下面这些内容。
```
Categories:
Flow content.
Phrasing content.
Embedded content.
If the element has a controls attribute: Interactive content.
Palpable content.
Contexts in which this element can be used:
Where embedded content is expected.
Content model:
If the element has a src attribute: zero or more track elements, then transparent, but with no media element descendants.
If the element does not have a src attribute: zero or more source elements, then zero or more track elements, then transparent, but with no media element descendants.
Tag omission in text/html:
Neither tag is omissible.
Content attributes:
Global attributes
src — Address of the resource
crossorigin — How the element handles crossorigin requests
poster — Poster frame to show prior to video playback
preload — Hints how much buffering the media resource will likely need
autoplay — Hint that the media resource can be started automatically when the page is loaded
playsinline — Encourage the user agent to display video content within the element's playback area
loop — Whether to loop the media resource
muted — Whether to mute the media resource by default
controls — Show user agent controls
width — Horizontal dimension
height — Vertical dimension
DOM interface:
[Exposed=Window, HTMLConstructor]
interface HTMLVideoElement : HTMLMediaElement {
[CEReactions] attribute unsigned long width;
[CEReactions] attribute unsigned long height;
readonly attribute unsigned long videoWidth;
readonly attribute unsigned long videoHeight;
[CEReactions] attribute USVString poster;
[CEReactions] attribute boolean playsInline;
};
```
我们看到这里的描述分为6个部分有下面这些内容。
- Categories标签所属的分类。
- Contexts in which this element can be used标签能够用在哪里。
- Content model标签的内容模型。
- Tag omission in text/html标签是否可以省略。
- Content attributes内容属性。
- DOM interface用WebIDL定义的元素类型接口。
这一节课我们关注一下Categories、Contexts in which this element can be used、Content model这几个部分。我会带你从标准中抓取数据做一个小工具用来检查X标签是否能放入Y标签内。
## 代码角度分析HTML标准
HTML标准描述用词非常的严谨这给我们抓取数据带来了巨大的方便首先我们打开单页面版HTML标准
- [https://html.spec.whatwg.org/](https://html.spec.whatwg.org/)
在这个页面上,我们执行一下以下代码:
```
Array.prototype.map.call(document.querySelectorAll(&quot;.element&quot;), e=&gt;e.innerText);
```
这样我们就得到了所有元素的定义了现在有107个元素。
不过比较尴尬的是这些文本中并不包含元素名我们只好从id属性中获取最后代码类似这样
```
var elementDefinations = Array.prototype.map.call(document.querySelectorAll(".element"), e =&gt; ({
text:e.innerText,
name:e.childNodes[0].childNodes[0].id.match(/the\-([\s\S]+)\-element:/)?RegExp.$1:null}));
```
接下来我们用代码理解一下这些文本。首先我们来分析一下这些文本它分成了6个部分而且顺序非常固定这样我们可以用JavaScript的正则表达式匹配来拆分六个字段。
我们这个小实验的目标是计算元素之间的包含关系因此我们先关心一下categories和contentModel两个字段。
```
for(let defination of elementDefinations) {
console.log(defination.name + ":")
let categories = defination.text.match(/Categories:\n([\s\S]+)\nContexts in which this element can be used:/)[1].split("\n");
for(let category of categories) {
console.log(category);
}
/*
let contentModel = defination.text.match(/Content model:\n([\s\S]+)\nTag omission in text\/html:/)[1].split("\n");
for(let line of contentModel)
console.log(line);
*/
}
```
接下来我们来处理category。
首先category的写法中最基本的就是直接描述了category的句子我们把这些不带任何条件的category先保存起来然后打印出来其它的描述看看
```
for(let defination of elementDefinations) {
//console.log(defination.name + ":")
let categories = defination.text.match(/Categories:\n([\s\S]+)\nContexts in which this element can be used:/)[1].split("\n");
defination.categories = [];
for(let category of categories) {
if(category.match(/^([^ ]+) content./))
defination.categories.push(RegExp.$1);
else
console.log(category)
}
/*
let contentModel = defination.text.match(/Content model:\n([\s\S]+)\nTag omission in text\/html:/)[1].split("\n");
for(let line of contentModel)
console.log(line);
*/
}
```
这里我们要处理的第一个逻辑是带if的情况。
然后我们来看看剩下的情况:
```
None.
Sectioning root.
None.
Sectioning root.
None.
Form-associated element.
Listed and submittable form-associated element.
None.
Sectioning root.
None.
If the type attribute is not in the Hidden state: Listed, labelable, submittable, resettable, and autocapitalize-inheriting form-associated element.
If the type attribute is in the Hidden state: Listed, submittable, resettable, and autocapitalize-inheriting form-associated element.
Listed, labelable, submittable, and autocapitalize-inheriting form-associated element.
Listed, labelable, submittable, resettable, and autocapitalize-inheriting form-associated element.
None.
Listed, labelable, submittable, resettable, and autocapitalize-inheriting form-associated element.
Listed, labelable, resettable, and autocapitalize-inheriting form-associated element.
Labelable element.
Sectioning root.
Listed and autocapitalize-inheriting form-associated element.
None.
Sectioning root.
None.
Sectioning root.
Script-supporting element.
```
这里出现了几个概念:
- None
- Sectioning root
- Form-associated element
- Labelable element
- Script-supporting element
如果我们要真正完美地实现元素分类,就必须要在代码中加入正则表达式来解析这些规则,这里作为今天的课后问题,留给你自己完成。
接下来我们看看Content Model我们照例先处理掉最简单点的部分就是带分类的内容模型
```
for(let defination of elementDefinations) {
//console.log(defination.name + ":")
let categories = defination.text.match(/Categories:\n([\s\S]+)\nContexts in which this element can be used:/)[1].split("\n");
defination.contentModel = [];
let contentModel = defination.text.match(/Content model:\n([\s\S]+)\nTag omission in text\/html:/)[1].split("\n");
for(let line of contentModel)
if(line.match(/^([^ ]+) content./))
defination.contentModel.push(RegExp.$1);
else
console.log(line)
}
```
好了,我们照例看看剩下了什么:
```
A head element followed by a body element.
If the document is an iframe srcdoc document or if title information is available from a higher-level protocol: Zero or more elements of metadata content, of which no more than one is a title element and no more than one is a base element.
Otherwise: One or more elements of metadata content, of which exactly one is a title element and no more than one is a base element.
Text that is not inter-element whitespace.
Nothing.
Text that gives a conformant style sheet.
One or more h1, h2, h3, h4, h5, h6 elements, optionally intermixed with script-supporting elements.
Nothing.
Zero or more li and script-supporting elements.
Either: Zero or more groups each consisting of one or more dt elements followed by one or more dd elements, optionally intermixed with script-supporting elements.
Or: One or more div elements, optionally intermixed with script-supporting elements.
Either: one figcaption element followed by flow content.
Or: flow content followed by one figcaption element.
Or: flow content.
If the element is a child of a dl element: one or more dt elements followed by one or more dd elements, optionally intermixed with script-supporting elements.
If the element is not a child of a dl element: flow content.
Transparent, but there must be no interactive content or a element descendants.
See prose.
Text.
If the element has a datetime attribute: Phrasing content.
Otherwise: Text, but must match requirements described in prose below.
Nothing.
Transparent.
Zero or more source elements, followed by one img element, optionally intermixed with script-supporting elements.
Nothing.
Zero or more param elements, then, transparent.
Nothing.
If the element has a src attribute: zero or more track elements, then transparent, but with no media element descendants.
If the element does not have a src attribute: zero or more source elements, then zero or more track elements, then transparent, but with no media element descendants.
If the element has a src attribute: zero or more track elements, then transparent, but with no media element descendants.
If the element does not have a src attribute: zero or more source elements, then zero or more track elements, then transparent, but with no media element descendants.
Nothing.
Transparent.
Nothing.
In this order: optionally a caption element, followed by zero or more colgroup elements, followed optionally by a thead element, followed by either zero or more tbody elements or one or more tr elements, followed optionally by a tfoot element, optionally intermixed with one or more script-supporting elements.
If the span attribute is present: Nothing.
If the span attribute is absent: Zero or more col and template elements.
Nothing.
Zero or more tr and script-supporting elements.
Zero or more td, th, and script-supporting elements.
Nothing.
Zero or more option, optgroup, and script-supporting elements.
Either: phrasing content.
Or: Zero or more option and script-supporting elements.
Zero or more option and script-supporting elements.
If the element has a label attribute and a value attribute: Nothing.
If the element has a label attribute but no value attribute: Text.
If the element has no label attribute and is not a child of a datalist element: Text that is not inter-element whitespace.
If the element has no label attribute and is a child of a datalist element: Text.
Text.
Optionally a legend element, followed by flow content.
One summary element followed by flow content.
Either: phrasing content.
Or: one element of heading content.
If there is no src attribute, depends on the value of the type attribute, but must match script content restrictions.
If there is a src attribute, the element must be either empty or contain only script documentation that also matches script content restrictions.
When scripting is disabled, in a head element: in any order, zero or more link elements, zero or more style elements, and zero or more meta elements.
When scripting is disabled, not in a head element: transparent, but there must be no noscript element descendants.
Otherwise: text that conforms to the requirements given in the prose.
Nothing (for clarification, see example).
Transparent
Transparent, but with no interactive content descendants except for a elements, img elements with usemap attributes, button elements, input elements whose type attribute are in the Checkbox or Radio Button states, input elements that are buttons, select elements with a multiple attribute or a display size greater than 1, and elements that would not be interactive content except for having the tabindex attribute specified.
```
这有点复杂我们还是把它做一些分类首先我们过滤掉带If的情况、Text和Transparent。
```
for(let defination of elementDefinations) {
//console.log(defination.name + ":")
let categories = defination.text.match(/Categories:\n([\s\S]+)\nContexts in which this element can be used:/)[1].split("\n");
defination.contentModel = [];
let contentModel = defination.text.match(/Content model:\n([\s\S]+)\nTag omission in text\/html:/)[1].split("\n");
for(let line of contentModel)
if(line.match(/([^ ]+) content./))
defination.contentModel.push(RegExp.$1);
else if(line.match(/Nothing.|Transparent./));
else if(line.match(/^Text[\s\S]*.$/));
else
console.log(line)
}
```
这时候我们再来执行看看:
```
A head element followed by a body element.
One or more h1, h2, h3, h4, h5, h6 elements, optionally intermixed with script-supporting elements.
Zero or more li and script-supporting elements.
Either: Zero or more groups each consisting of one or more dt elements followed by one or more dd elements, optionally intermixed with script-supporting elements.
Or: One or more div elements, optionally intermixed with script-supporting elements.
If the element is a child of a dl element: one or more dt elements followed by one or more dd elements, optionally intermixed with script-supporting elements.
See prose.
Otherwise: Text, but must match requirements described in prose below.
Zero or more source elements, followed by one img element, optionally intermixed with script-supporting elements.
Zero or more param elements, then, transparent.
If the element has a src attribute: zero or more track elements, then transparent, but with no media element descendants.
If the element does not have a src attribute: zero or more source elements, then zero or more track elements, then transparent, but with no media element descendants.
If the element has a src attribute: zero or more track elements, then transparent, but with no media element descendants.
If the element does not have a src attribute: zero or more source elements, then zero or more track elements, then transparent, but with no media element descendants.
In this order: optionally a caption element, followed by zero or more colgroup elements, followed optionally by a thead element, followed by either zero or more tbody elements or one or more tr elements, followed optionally by a tfoot element, optionally intermixed with one or more script-supporting elements.
If the span attribute is absent: Zero or more col and template elements.
Zero or more tr and script-supporting elements.
Zero or more td, th, and script-supporting elements.
Zero or more option, optgroup, and script-supporting elements.
Or: Zero or more option and script-supporting elements.
Zero or more option and script-supporting elements.
If the element has a label attribute but no value attribute: Text.
If the element has no label attribute and is not a child of a datalist element: Text that is not inter-element whitespace.
If the element has no label attribute and is a child of a datalist element: Text.
When scripting is disabled, in a head element: in any order, zero or more link elements, zero or more style elements, and zero or more meta elements.
When scripting is disabled, not in a head element: transparent, but there must be no noscript element descendants.
Otherwise: text that conforms to the requirements given in the prose.
```
这下剩余的就少多了我们可以看到基本上剩下的都是直接描述可用的元素了如果你愿意还可以用代码进一步解析不过如果是我的话会选择手工把它们写成JSON了毕竟只有三十多行文本。
好了有了contentModel和category我们要检查某一元素是否可以作为另一元素的子元素就可以判断一下两边是否匹配啦首先我们要做个索引
```
var dictionary = Object.create(null);
for(let defination of elementDefinations) {
dictionary[defination.name] = defination;
}
```
然后我们编写一下我们的check函数
```
function check(parent, child) {
for(let category of child.categories)
if(parent.contentModel.categories.conatains(category))
return true;
if(parent.contentModel.names.conatains(child.name))
return true;
return false;
}
```
## 总结
这一节课我们完成了一个小实验利用工具分析Web标准文本来获得元素的信息。
通过这个实验我希望能够传递一种思路代码能够帮助我们从Web标准中挖掘出来很多想要的信息编写代码的过程也是更深入理解标准的契机。
我们前面的课程中把元素分成了几类来讲解,但是这些分类只能大概地覆盖所有的标签,我设置课程的目标也是讲解标签背后的知识,而非每一种标签的细节。具体每一种标签的属性和细节,可以留给大家自己去整理。
这一节课的产出,则是“绝对完整的标签列表”,也是我学习和阅读标准的小技巧,通过代码我们可以从不同的侧面分析标准的内容,挖掘需要注意的点,这是一种非常好的学习方法。

View File

@@ -0,0 +1,207 @@
<audio id="audio" title="HTML替换型元素为什么link一个CSS要用href而引入js要用src呢" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/44/f9/442f9692a3a382c7f40d1bcf0a090bf9.mp3"></audio>
你好我是winter。我们今天来讲讲替换型元素。
我们都知道一个常识,一个网页,它是由多个文件构成的,我们在之前的课程中,已经学过了一种引入文件的方案:链接。
这节课我们要讲的替换型元素,就是另一种引入文件的方式了。替换型元素是把文件的内容引入,替换掉自身位置的一类标签。
我们首先来看一种比较熟悉的标签script标签。
## script
我们之所以选择先讲解script标签是因为script标签是为数不多的既可以作为替换型标签又可以不作为替换型标签的元素。
我们先来看看script标签的两种用法
```
&lt;script type="text/javascript"&gt;
console.log("Hello world!");
&lt;/script&gt;
&lt;script type="text/javascript" src="my.js"&gt;&lt;/script&gt;
```
这个例子中我们展示了两种script标签的写法一种是直接把脚本代码写在script标签之间另一种是把代码放到独立的js文件中用src属性引入。
这两种写法是等效的。我想这种等效性可以帮助你理解替换型元素的“替换”是怎么一回事。
这里我们就可以回答标题中的问题了凡是替换型元素都是使用src属性来引用文件的而我们之前的课程中已经讲过链接型元素是使用href标签的。
虽然我不知道当初是怎么设计的但是style标签并非替换型元素不能使用src属性这样我们用link标签引入CSS文件当然就是用href标签啦。
接下来我们再看看别的替换型元素先来了解一下img标签。
## img
毫无疑问我们最熟悉的替换型标签就是img标签了几乎每个前端都会日常使用img标签。
img标签的作用是引入一张图片。这个标签是没有办法像script标签那样作为非替换型标签来使用的它必须有src属性才有意义。
如果一定不想要引入独立文件可以使用data uri我们来看个实际的例子
```
&lt;img src='data:image/svg+xml;charset=utf8,&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg"&gt;&lt;rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/&gt;&lt;/svg&gt;'/&gt;
```
这个例子中我们使用了data uri作为图片的src这样并没有产生独立的文件客观上做到了和内联相同的结果这是一个常用的技巧。
img标签可以使用width和height指定宽度和高度。也可以只指定其中之一。我们看个例子
```
&lt;img src='data:image/svg+xml;charset=utf8,&lt;svg width="600" height="400" version="1.1"
xmlns="http://www.w3.org/2000/svg"&gt;&lt;ellipse cx="300" cy="150" rx="200" ry="80"
style="fill:rgb(200,100,50);
stroke:rgb(0,0,100);stroke-width:2"/&gt;&lt;/svg&gt;' width="100"/&gt;
```
这个例子中,为了方便你理解,我们把图片换成了椭圆,我们可以看到,当我们指定了宽度后,图片被**等比例缩放了**。这个特性非常重要,适用于那种我们既要限制图片尺寸,又要保持图片比例的场景。
如果从性能的角度考虑,建议你同时给出图片的宽高,因为替换型元素加载完文件后,如果尺寸发生变换,会触发重排版(这个概念我们在浏览器原理部分已经讲过,可以复习一下)。
此处要重点提到一个属性alt属性这个属性很难被普通用户感知对于视障用户非常重要可以毫不夸张地讲给img加上alt属性已经做完了可访问性的一半。
img标签还有一组重要的属性那就是srcset和sizes它们是src属性的升级版所以我们前面讲img标签必须有src属性这是不严谨的说法
这两个属性的作用是在不同的屏幕大小和特性下使用不同的图片源。下面一个例子也来自MDN它展示了srcset和sizes的用法
```
&lt;img srcset="elva-fairy-320w.jpg 320w,
elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy"&gt;
```
srcset提供了根据屏幕条件选取图片的能力但是其实更好的做法是使用picture元素。
## picture
picture元素可以根据屏幕的条件为其中的img元素提供不同的源它的基本用法如下
```
&lt;picture&gt;
&lt;source srcset="image-wide.png" media="(min-width: 600px)"&gt;
&lt;img src="image-narrow.png"&gt;
&lt;/picture&gt;
```
picture元素的设计跟audio和video保持了一致稍后我会为你讲解这两个元素它跟img搭配srcset和sizes不同它使用source元素来指定图片源并且支持多个。
这里的media属性是media query跟CSS的@media规则一致
## video
在HTML5早期的设计中video标签跟img标签类似也是使用src属性来引入源文件的不过我想应该是考虑到了各家浏览器支持的视频格式不同现在的video标签跟picture元素一样也是提倡使用source的。
下面例子是一个古典的video用法
```
&lt;video controls="controls" src="movie.ogg"&gt;
&lt;/video&gt;
```
这个例子中的代码用src来指定视频的源文件。但是因为一些历史原因浏览器对视频的编码格式兼容问题分成了几个派系这样对于一些兼容性要求高的网站我们使用单一的视频格式是不合适的。
现在的video标签可以使用source标签来指定接入多个视频源。
```
&lt;video controls="controls" &gt;
&lt;source src="movie.webm" type="video/webm" &gt;
&lt;source src="movie.ogg" type="video/ogg" &gt;
&lt;source src="movie.mp4" type="video/mp4"&gt;
You browser does not support video.
&lt;/video&gt;
```
从这个例子中我们可以看到source标签除了支持media之外还可以使用type来区分源文件的使用场景。
video标签的内容默认会被当做不支持video的浏览器显示的内容吗因此如果要支持更古老的浏览器还可以在其中加入object或者embed标签这里就不详细展开了。
video中还支持一种标签track。
track是一种播放时序相关的标签它最常见的用途就是字幕。track标签中必须使用 srclang 来指定语言此外track具有kind属性共有五种。
- subtitles就是字幕了不一定是翻译也可能是补充性说明。
- captions报幕内容可能包含演职员表等元信息适合听障人士或者没有打开声音的人了解音频内容。
- descriptions视频描述信息适合视障人士或者没有视频播放功能的终端打开视频时了解视频内容。
- chapters用于浏览器视频内容。
- metadata给代码提供的元信息对普通用户不可见。
一个完整的video标签可能会包含多种track和多个source这些共同构成了一个视频播放所需的全部信息。
## audio
接下来我们来讲讲audio跟picture和video两种标签一样audio也可以使用source元素来指定源文件。我们看一下例子
```
&lt;audio controls&gt;
&lt;source src="song.mp3" type="audio/mpeg"&gt;
&lt;source src="song.ogg" type="audio/ogg"&gt;
&lt;p&gt;You browser does not support audio.&lt;/p&gt;
&lt;/audio&gt;
```
但比起videoaudio元素的历史问题并不严重所以使用src也是没有问题的。
## iframe
最后我们来讲一下iframe这个标签能够嵌入一个完整的网页。
不过在移动端iframe受到了相当多的限制它无法指定大小里面的内容会被完全平铺到父级页面上。
同时很多网页也会通过http协议头禁止自己被放入iframe中。
iframe标签也是各种安全问题的重灾区。opener、window.name、甚至css的opacity都是黑客可以利用的漏洞。
因此在2019年当下这个时间点任何情况下我都不推荐在实际开发中用以前的iframe。
当然不推荐使用是一回事因为没人能保证不遇到历史代码我们还是应该了解一下iframe的基本用法
```
&lt;iframe src="http://time.geekbang.org"&gt;&lt;/iframe&gt;
```
这个例子展示了古典的iframe用法。
在新标准中为iframe加入了sandbox模式和srcdoc属性这样给iframe带来了一定的新场景。我们来看看例子
```
&lt;iframe sandbox srcdoc="&lt;p&gt;Yeah, you can see it &lt;a href="/gallery?mode=cover&amp;amp;amp;page=1"&gt;in my gallery&lt;/a&gt;."&gt;&lt;/iframe&gt;
```
这个例子中使用srcdoc属性创建了一个新的文档嵌入在iframe中展示并且使用了sandbox来隔离。
这样这个iframe就不涉及任何跨域问题了。
## 总结
这节课我们又认识了一组HTML元素替换型元素。它们的特点是引入一个外部资源来进入页面替换掉自身的位置。
我们通过对script、img、picture、audio、video、iframe几个标签的讲解了解了不同的资源引入方式
- src属性
- srcset属性
- source标签
- srcdoc属性。
这中间我们也介绍了一些小技巧比如src属性的好朋友data uri这在实际开发中非常有用。
最后留给你一个小问题请查资料总结一下在多数现代浏览器兼容的范围内src属性支持哪些协议的uri如http和我们提到的data

View File

@@ -0,0 +1,303 @@
<audio id="audio" title="HTML语义div和span不是够用了吗" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/74/54/74b3c265c45e2a0187dfd86fdd267054.mp3"></audio>
你好我是winter。
今天这篇是我们正式开篇的第一篇文章我想和你聊聊HTML。
我猜屏幕那一边的你估计会说“HTML我很熟悉了每天写这不是初级程序员才学的内容么这我还能不会吗
其实在我看来HTML并不简单它是典型的“入门容易精通困难”的一部分知识。深刻理解HTML是成为优秀的前端工程师重要的一步。
我们在上一篇文章中讲到了HTML的标签可以分为很多种比如head里面的元信息类标签又比如img、video、audio之类的替换型媒体标签。我今天要讲的标签是语义类标签。
## 语义类标签是什么,使用它有什么好处?
语义类标签也是大家工作中经常会用到的一类标签它们的特点是视觉表现上互相都差不多主要的区别在于它们表示了不同的语义比如大家会经常见到的section、nav、p这些都是语义类的标签。
语义是我们说话表达的意思,多数的语义实际上都是由文字来承载的。语义类标签则是纯文字的补充,比如标题、自然段、章节、列表,这些内容都是纯文字无法表达的,我们需要依靠语义标签代为表达。
在讲语义之前,我们来说说为什么要用语义。
现在我们很多的前端工程师写起代码来,多数都不用复杂的语义标签, 只靠div 和 span 就能走天下了。
这样做行不行呢?毫无疑问答案是行。那这样做好不好呢?按照正确的套路,我应该说不好,但是在很多情况下,答案其实是好。
这是因为在现代互联网产品里HTML用于描述“软件界面”多过于“富文本”而软件界面里的东西实际上几乎是没有语义的。比如说我们做了一个购物车功能我们一定要给每个购物车里的商品套上ul吗比如说加入购物车这个按钮我们一定要用Button吗
实际上我觉得没必要因为这个场景里面跟文本中的列表以及表单中的Button其实已经相差很远了所以我支持在任何“软件界面”的场景中直接使用div和span。
**不过,在很多工作场景里,语义类标签也有它们自己无可替代的优点。正确地使用语义标签可以带来很多好处。**
- 语义类标签对开发者更为友好使用语义类标签增强了可读性即便是在没有CSS的时候开发者也能够清晰地看出网页的结构也更为便于团队的开发和维护。
- 除了对人类友好之外语义类标签也十分适宜机器阅读。它的文字表现力丰富更适合搜索引擎检索SEO也可以让搜索引擎爬虫更好地获取到更多有效信息有效提升网页的搜索量并且语义类还可以支持读屏软件根据文章可以自动生成目录等等。
不过不恰当地使用语义标签反而会造成负面作用。这里我们举一个常见的误区作为例子。我们都知道ul是无序列表ol是有序列表所以很多接触过语义这个概念半懂不懂的前端工程师特别喜欢给所有并列关系的元素都套上ul。
实际上, ul 是长成下面的这种样子的(以下来自HTML标准)。
I have lived in the following countries:
- Switzerland
- Norway
- United Kingdom
- United States
ul多数出现正在行文中间它的上文多数在提示要列举某些项。但是如果所有并列关系都用ul会造成大量冗余标签。
错误地使用语义标签会给机器阅读造成混淆、增加嵌套给CSS编写加重负担。
所以,对于语义标签,**我的态度是:“用对”比“不用”好,“不用”比“用错”好。当然了,我觉得有理想的前端工程师还是应该去追求“用对”它们。**
与JavaScript这样严格的编程语言相比HTML中语义标签的使用更接近我们平常说话用的自然语言。我们说话并没有唯一的标准措辞语义标签的使用也是一样。下面我挑选了几种我认为比较重要的语义标签使用场景来为你介绍一下。
## 作为自然语言延伸的语义类标签
其实语义问题不仅仅属于理科,它还是个文科问题。
**所以我们这里讲语义标签的使用的第一个场景,也是最自然的使用场景,就是:作为自然语言和纯文本的补充,用来表达一定的结构或者消除歧义。**
我们先来看看“表达一定的结构”这个场景。
在日语中有一个语法现象叫做ルビ它的读音是ruby著名的ruby语言就是据此命名的它中文的意思大约类似于注音或者意思的注解它的形式可以看下图
<img src="https://static001.geekbang.org/resource/image/d4/45/d464f16955d1629b9911699e99d03f45.png" alt="">
图中的例子选自动画片《某科学的超电磁炮》第二季第一话。图中把teleport放在空间移动上方的用法就是日文中ruby的用法。“空间移动”是动画中白井黑子的技能这里动画字幕上写的是“空间移动”动画里的台词则用了英文发音“Teleport”这里就形成了一个使用ruby的场景。
ruby的这个形式在中国的网友中间最近被玩出了新花样比如表情包。
<img src="https://static001.geekbang.org/resource/image/67/15/67e0027f9a35eac8170b758f420ff815.jpeg" alt="">
有时候微信聊天不能用ruby这样的东西真的是好急啊只好用括号代替效果真是差了不少。
在HTML5中就引入了这个表示ruby的标签它由ruby、rt、rp三个标签来实现。
所以说这些情况里存在的语义其实原本就存在了只是我们用纯文字是没法表达的HTML作为一种“超文本”语言支持这些文字表达就是必要的了。
**还有一种情况是HTML的有些标签实际上就是必要的甚至必要的程度可以达到如果没有这个标签文字会产生歧义的程度。**
这里我们可以介绍一下em标签。
```
今天我吃了一个苹果.
```
我们看看这句话,看上去它很清楚,但是实际上,这句话放到不同上下文中,可能表达完全不同的意思。
```
昨天我吃了一个香蕉。
今天我吃了一个苹果。
```
再比如:
```
昨天我吃了两个苹果。
今天我吃了一个苹果。
```
试着读一读,这两段里面的“今天我吃了一个苹果”,你是不是发现读音不自觉地发生了变化?
实际上,不仅仅是读音,这里的意思也发生了变化。前一段中,表示我今天吃的是苹果,而不是别的什么东西,后一段中,则表示我今天只吃了一个苹果,没有多吃。
当没有上下文时如何消除歧义呢这就要用到我们的em标签了。em表示重音
```
今天我吃了一个&lt;em&gt;苹果&lt;/em&gt;。
今天我吃了&lt;em&gt;一个&lt;/em&gt;苹果。
```
通过em标签我们可以消除这样的歧义。
一些文章常常会拿em和strong做对比实际上我们只要理解了em的真正意思它和strong可谓天差地别并没有任何混淆的可能。
## 作为标题摘要的语义类标签
介绍完自然语言的语义场景后,我想介绍的另一个语义重要使用场景,就是文章的结构。中国古代小说就形成了“章-回”的概念,西方的戏剧也有幕的区分,所以人类的自然语言作品也是如出一辙。
HTML也应该支持这样的需求。HTML语义标签中有不少是用于支持这样的结构的标签。
语义化的HTML能够支持自动生成目录结构HTML标准中还专门规定了生成目录结构的算法即使我们并不打算深入实践语义也应该尽量在大的层面上保证这些元素的语义化使用。
首先我们需要形成一个概念一篇文档会有一个树形的目录结构它由各个级别的标题组成。这个树形结构可能不会跟HTML元素的嵌套关系一致。
```
例如:
&lt;h1&gt;HTML语义&lt;/h1&gt;
&lt;p&gt;balah balah balah balah&lt;/p&gt;
&lt;h2&gt;弱语义&lt;/h2&gt;
&lt;p&gt;balah balah&lt;/p&gt;
&lt;h2&gt;结构性元素&lt;/h2&gt;
&lt;p&gt;balah balah&lt;/p&gt;
......
```
这段HTML几乎是平铺的元素但是它的标题结构是
<li>HTML语义
<ul>
- 弱语义
- 结构性元素
- ……
h1-h6是最基本的标题它们表示了文章中不同层级的标题。有些时候我们会有副标题为了避免副标题产生额外的一个层级我们使用hgroup标签。
我们来看下有/无hgroup的对比
```
&lt;h1&gt;JavaScript对象&lt;/h1&gt;
&lt;h2&gt;我们需要模拟类吗?&lt;/h2&gt;
&lt;p&gt;balah balah&lt;/p&gt;
......
```
此段生成以下标题结构:
<li>JavaScript对象
<ul>
- 我们需要模拟类吗?
-
```
&lt;hgroup&gt;
&lt;h1&gt;JavaScript对象&lt;/h1&gt;
&lt;h2&gt;我们需要模拟类吗?&lt;/h2&gt;
&lt;/hgroup&gt;
&lt;p&gt;balah balah&lt;/p&gt;
......
```
这一段生成以下标题结构:
<li>JavaScript对象——我们需要模拟类吗
<ul>
-
我们通过两个效果的对比就可以知道在hgroup中的h1-h6被视为同一标题的不同组成部分。
从HTML 5开始我们有了section标签这个标签可不仅仅是一个“有语义的div”它会改变h1-h6的语义。section的嵌套会使得其中的h1-h6下降一级因此在HTML5以后我们只需要section和h1就足以形成文档的树形结构
```
&lt;section&gt;
&lt;h1&gt;HTML语义&lt;/h1&gt;
&lt;p&gt;balah balah balah balah&lt;/p&gt;
&lt;section&gt;
&lt;h1&gt;弱语义&lt;/h1&gt;
&lt;p&gt;balah balah&lt;/p&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h1&gt;结构性元素&lt;/h1&gt;
&lt;p&gt;balah balah&lt;/p&gt;
&lt;/section&gt;
......
&lt;/section&gt;
```
这段代码同样会形成前面例子的标题结构:
<li>HTML语义
<ul>
- 弱语义
- 结构性元素
- ……
## 作为整体结构的语义类标签
我们想介绍的最后一个场景是随着越来越多的浏览器推出“阅读模式”以及各种非浏览器终端的出现语义化的HTML适合机器阅读的特性变得越来越重要。
应用了语义化结构的页面,可以明确地提示出页面信息的主次关系,它能让浏览器很好地支持“阅读视图功能”,还可以让搜索引擎的命中率提升,同时,它也对视障用户的读屏软件更友好。
我们正确使用整体结构类的语义标签可以让页面对机器更友好。比如这里一个典型的body类似这样
```
&lt;body&gt;
&lt;header&gt;
&lt;nav&gt;
……
&lt;/nav&gt;
&lt;/header&gt;
&lt;aside&gt;
&lt;nav&gt;
……
&lt;/nav&gt;
&lt;/aside&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;footer&gt;
&lt;address&gt;……&lt;/address&gt;
&lt;/footer&gt;
&lt;/body&gt;
```
在body下面有一个headerheader里面是一个nav跟header同级的有一个asideaside里面也有一个nav。接下来是文章的整体也就是一个一个的section。section里面可能还有嵌套但是我们就不管了最后是一个footer这个footer里面可能有address这样的内容。
除此之外还有articlearticle是一种特别的结构它表示具有一定独立性质的文章。所以article和body具有相似的结构同时一个HTML页面中可能有多个article存在。
一个典型的场景是多篇新闻展示在同一个新闻专题页面中这种类似报纸的多文章结构适合用article来组织。
```
&lt;body&gt;
&lt;header&gt;……&lt;/header&gt;
&lt;article&gt;
&lt;header&gt;……&lt;/header&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;section&gt;……&lt;/section&gt;
&lt;footer&gt;……&lt;/footer&gt;
&lt;/article&gt;
&lt;article&gt;
……
&lt;/article&gt;
&lt;article&gt;
……
&lt;/article&gt;
&lt;footer&gt;
&lt;address&gt;&lt;/address&gt;
&lt;/footer&gt;
&lt;/body&gt;
```
body里面有自己的header和footer然后里面是竖篇的article每一个article里面都有自己的header、section、footer。这是一个典型的多文章结构。
在这个结构里,我们看到了一些新标签,我也来逐个介绍一下。
- header如其名通常出现在前部表示导航或者介绍性的内容。
- footer通常出现在尾部包含一些作者信息、相关链接、版权信息等。
header和footer一般都是放在article或者body的直接子元素但是标准中并没有明确规定footer也可以和asidenavsection相关联header不存在关联问题
- aside表示跟文章主体不那么相关的部分它可能包含导航、广告等工具性质的内容。
aside很容易被理解为侧边栏实际上二者是包含关系侧边栏是asideaside不一定是侧边栏。
aside和header中都可能出现导航nav标签二者的区别是header中的导航多数是到文章自己的目录而aside中的导航多数是到关联页面或者是整站地图。
最后footer中包含address这是个非常容易被误用的标签。address并非像date一样表示一个给机器阅读的地址而是表示“文章作者的联系方式”address明确地只关联到article和body。
## 总结
本篇中我们介绍了一些基本原则和HTML文档的整体结构从整体上了解了HTML语义。
至此,我们可以回答是否要语义化的问题:我们应该分开一些场景来看语义,把它用在合适的场景下,可以获得额外的效果。本篇文中,我们至少涉及了三个明确的场景:
- 自然语言表达能力的补充;
- 文章标题摘要;
- 适合机器阅读的整体结构。
下一篇中,我们会继续深入到更细致的结构中,进一步了解语义。你在工作中是否在使用语义化的标签开发?学习过本篇之后,答案有没有变化呢?你可以给我留言,我们一起讨论。
# 猜你喜欢
[<img src="https://static001.geekbang.org/resource/image/1a/08/1a49758821bdbdf6f0a8a1dc5bf39f08.jpg" alt="unpreview">](https://time.geekbang.org/course/intro/163?utm_term=zeusMTA7L&amp;utm_source=app&amp;utm_medium=chongxueqianduan&amp;utm_campaign=163-presell)

View File

@@ -0,0 +1,279 @@
<audio id="audio" title="HTML语义如何运用语义类标签来呈现Wiki网页" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/42/3b/424db97fd6e0a766176add860d837c3b.mp3"></audio>
你好我是winter今天我们继续来聊聊HTML模块的语义类标签。
在上一篇文章中,我花了大量的篇幅和你解释了正确使用语义类标签的好处和一些场景。那么,哪些场景适合用到语义类标签呢,又如何运用语义类标签呢?
不知道你还记不记得在大学时代你被导师逼着改毕业论文格式的情景如果你回想一下你在论文中使用的那些格式你会发现其实它们都是可以用HTML里的语义标签来表示的。
这正是因为HTML最初的设计场景就是“超文本”早期HTML工作组的专家都是出版界书籍排版的专家。
所以在这一部分我们找了个跟论文很像的案例Wikipedia文章这种跟论文相似的网站比较适合用来学习语义类标签。通过分析一篇Wiki的文章用到的语义类标签来进一步帮你理解语义的概念。
你可以在电脑上,打开这个页面:
- [https://en.wikipedia.org/wiki/World_Wide_Web](https://en.wikipedia.org/wiki/World_Wide_Web)
为了防止这个页面被修改,我们保存了一个副本:
- [http://static001.geekbang.org/static/time/quote/World_Wide_Web-Wikipedia.html](http://static001.geekbang.org/static/time/quote/World_Wide_Web-Wikipedia.html)
这是一篇我们选择的Wiki文章虽然在原本的Wikipedia网站中也是大量使用了div和span来完成功能。在这里我们来尝试分析一下应该如何用语义类标签来呈现这样的一个页面/文章。
我们看一下这个页面。
## aside
<img src="https://static001.geekbang.org/resource/image/b6/da/b692ade1e78d295de52ffe01edaa11da.png" alt="">
首先我们来看下左侧侧边栏根据上一篇文章中提到的语义定义这里属于aside内容。是导航性质的工具内容。
## article
<img src="https://static001.geekbang.org/resource/image/cf/aa/cfc9a6542e0fc973e6e871043e7e42aa.jpeg" alt="">
我们来到文章主体部分因为主体部分具有明确的独立性所以可以用article来包裹。
## hgroup, h1, h2
<img src="https://static001.geekbang.org/resource/image/7d/48/7ddad196e7734fd32bfc577b3a459c48.jpeg" alt="">
在语义的上一篇文章中我们介绍过hgroup和h1-h6的作用hgroup是标题组h1是一级标题h2是二级标题。这里World Wide Web 是文章的大标题适合h1元素。
接下来出现了一个副标题。From Wikipedia, the free encyclopedia。这个地方适合使用h2跟h1组成一个hgroup所以代码可能是类似这样的:
```
&lt;hgroup&gt;
&lt;h1&gt;World Wide Web &lt;/h1&gt;
&lt;h2&gt;From Wikipedia, the free encyclopedia&lt;/h2&gt;
&lt;/hgroup&gt;
```
## abbr
<img src="https://static001.geekbang.org/resource/image/13/72/139b1603d3851b11e9ee4ed955aec972.png" alt="">
abbr标签表示缩写。考虑到WWW是World Wide Web的缩写所以文中所有出现的WWW都应该使用abbr标签。
```
&lt;abbr title="World Wide Web"&gt;WWW&lt;/abbr&gt;.
```
## hr
<img src="https://static001.geekbang.org/resource/image/3e/1e/3e3fca7df41dd824da47efca4aa2731e.jpeg" alt="">
细心的同学会发现在Wiki的界面中出现了一条很长的横线大家都知道hr标签表示横向分隔线那么这个地方是不是应该用hr呢
答案是不用。我们读一下标准的定义就知道了hr表示故事走向的转变或者话题的转变显然此处两个标题并非这种关系所以我们应该使用CSS的border来把它当作纯视觉效果来实现所以这里是不需要用hr的。
## p
<img src="https://static001.geekbang.org/resource/image/a5/d4/a5c22955f87e2861cadfa3fdb15565d4.jpeg" alt="">
接下来一段我们看到了三段“note”也就是注记。它在文章中用作额外注释。
>
“WWW” and “The Web” redirect here. For other uses of WWW, see WWW (disambiguation). For other uses of web, see Web (disambiguation).
For the first web software, see WorldWideWeb.
Not to be confused with the Internet.
HTML中并没有note相关的语义所以我们用普通的p标签加上`class="note"`来实现。后面的多数自然段都是普通的段落我们用p标签来实现。
## strong
<img src="https://static001.geekbang.org/resource/image/d7/a1/d7f8b1f98df1488813c3fc2d6b06d5a1.jpeg" alt="">
注意,这里 “World Wide Web (WWW)” 和 “the Web” 使用了黑体呈现从上下文来看这里表示这个词很重要所以我们使用strong标签。
```
&lt;p&gt;
A global map of the web index for countries in 2014
&lt;strong&gt;The World Wide Web (WWW)&lt;/strong&gt;, also called &lt;strong&gt;the Web&lt;/strong&gt;,
......
```
## blockquote, q, cite
<img src="https://static001.geekbang.org/resource/image/e5/1a/e516e5e00ecc5b6b0b743dd2a8d65d1a.png" alt="">
接下来我们看到了一个论文中很常见的用法“引述”。
>
interlinked by hypertext links, and accessible via the Internet.[1]
注意看这里的[1],当我们把鼠标放上去的时候,出现了引述的相关信息:
>
“What is the difference between the Web and the Internet?”. W3C Help and FAQ. W3C. 2009. Archived from the original on 9 July 2015. Retrieved 16 July 2015.
在HTML中有三个跟引述相关的标签blockquote表示段落级引述内容q表示行内的引述内容cite表示引述的作品名。
这里的作品名称 “What is the difference between the Web and the Internet?”应当使用cite标签。
```
&lt;cite&gt;&quot;What is the difference between the Web and the Internet?&quot;&lt;/cite&gt;. W3C Help and FAQ. W3C. 2009. Archived from the original on 9 July 2015. Retrieved 16 July 2015.
```
在文章的结尾处,有对应的 References 一节这一节中所有的作品名称也应该加入cite标签。
<img src="https://static001.geekbang.org/resource/image/31/45/31246e3ebf6426bfd6b1373a0644b245.png" alt="">
这里我们看看引用的原文就可以知道Wiki文章中的信息并非直接引用如果是直接引用的内容那么我们还应该加上blockquote或者q标签。
## time
<img src="https://static001.geekbang.org/resource/image/95/b6/9573647112ae3812013b37c29aa7d2b6.png" alt="">
这里除了引用的文章外还出现了日期为了让机器阅读更加方便可以加上time标签
```
&lt;cite&gt;&quot;What is the difference between the Web and the Internet?&quot;&lt;/cite&gt;. W3C Help and FAQ. W3C. 2009. Archived from the original on &lt;time datetime=&quot;2015-07-09&quot;&gt;9 July 2015&lt;/time&gt;. Retrieved &lt;time datetime=&quot;2015-07-16&quot;&gt;16 July 2015&lt;/time&gt;.
```
## figure, figcaption
<img src="https://static001.geekbang.org/resource/image/6d/72/6d473b6fb734ea85a8cc209bc1716b72.png" alt="">
我们注意一下文章的右侧出现了几张图片这种出现在文中的图片不仅仅是一个img标签它和下面的文字组成了一个figure的语法现象figure也是我们的一种标签用于表示与主文章相关的图像、照片等流内容
```
&lt;figure&gt;
&lt;img src=&quot;https://.....440px-NeXTcube_first_webserver.JPG&quot;/&gt;
&lt;figcaption&gt;The NeXT Computer used by Tim Berners-Lee at CERN.&lt;/figcaption&gt;
&lt;/figure&gt;
```
这种插入文章中的内容不仅限图片代码、表格等只要是具有一定自包含性类似独立句子的内容都可以用figure。这里面我们用figcaption表示内容的标题当然也可以没有标题。
## dfn
<img src="https://static001.geekbang.org/resource/image/b7/19/b7ae53127450b496729edd459cbc0619.png" alt="">
然后我们继续往下看,来注意这一句:
>
The terms Internet and World Wide Web are often used without much distinction. However, the two are not the same. The Internet is a global system of interconnected computer networks. In contrast, the World Wide Web is a global collection of documents and other resources, linked by hyperlinks and URIs.
这里分别定义了Internet和World Wide Web我们应该使用dfn标签。
```
The terms Internet and World Wide Web are often used without much distinction. However, the two are not the same.
The &lt;dfn&gt;Internet&lt;/dfn&gt; is a global system of interconnected computer networks.
In contrast, the &lt;dfn&gt;World Wide Web&lt;/dfn&gt; is a global collection of documents and other resources, linked by hyperlinks and URIs.
```
代码中你可以看见你需要在你要定义的词前后放上dfn标签所以我们知道了dfn标签是用来包裹被定义的名词。
## nav, ol, ul
<img src="https://static001.geekbang.org/resource/image/c1/f6/c12c129af98f6aa99b7dcdbdef1f62f6.png" alt="">
接下来几个普通的段落之后我们看到了文章的目录。这里的目录链接到文章的各个章节我们可以使用nav标签。因为这里的目录顺序不可随意变化所以我们这里使用多级的ol结构。
```
&lt;nav&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;...&quot;&gt;History&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;...&quot;&gt;Function&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;...&quot;&gt;Linking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;...&quot;&gt;Dynamic updates of web pages&lt;/a&gt;&lt;/li&gt;
...
&lt;/ol&gt;
&lt;/li&gt;
...
&lt;/ol&gt;
&lt;/nav&gt;
```
我们这里必须要指出ol和ul的区分是内容是否有顺序关系每一项的前面不论是数字还是点都不会影响语义的判断。所以你可以注意一下这里不要因为视觉表现效果而改变语义的使用。
## pre, samp, code
<img src="https://static001.geekbang.org/resource/image/ab/ed/ab5be608e3b4d2bd15b79c5b8885a2ed.png" alt="">
继续往下我们来到了这里我们看见这篇文章有一个很重要的特色文章中嵌入了一些代码和一些预先编写好的段落。我们看到在“Function”小节中有一段背景色是灰色的文字。
```
GET /home.html HTTP/1.1
Host: www.example.org
```
这是一段HTTP协议的内容描述因为这段内容的换行是非常严格的所以我们不需要浏览器帮我们做自动换行因此我们使用了pre标签表示这部分内容是预先排版过的不需要浏览器进行排版。
又因为这是一段计算机程序的示例输出所以我们可以使用samp标签
```
&lt;pre&gt;&lt;samp&gt;
GET /home.html HTTP/1.1
Host: www.example.org
&lt;/samp&gt;&lt;/pre&gt;
```
接下来Wiki中的内容出现了一段HTML代码我们同样不希望浏览器做自动换行。
```
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Example.org The World Wide Web&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;The World Wide Web, abbreviated as WWW and commonly known ...&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
```
因为同时是代码我们还需要加上code标签。最后的代码是pre标签包裹了code标签code标签包裹了HTML代码。
```
&lt;pre&gt;&lt;code&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Example.org The World Wide Web&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;p&amp;gt;The World Wide Web, abbreviated as WWW and commonly known ...&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
```
在后面的代码中还有一些在行内的code比如 title和 p括起来的内容这些也都应该使用code标签。
### 总结
在这一篇Wiki文章中已经涉及了大部分语义标签可见HTML工作组对语义标签的增加是非常谨慎和保守的。
当然了,我们选择的案例不可能刚巧覆盖所有的标签,还有些没讲到的标签,我们这里稍微做一下简要的补充说明。
<img src="https://static001.geekbang.org/resource/image/96/9e/9684130e423b6734b23652f4f0b6359e.jpg" alt="">
(长按点击大图查看)
实际上HTML这种语言并不像严谨的编程语言一样有一条非此即彼的线。一些语义的使用其实会带来争议所以我的建议是你可以尽量只用自己熟悉的语义标签并且只在有把握的场景引入语义标签。这样我们才能保证语义标签不被滥用造成更多的问题。
你最擅长使用哪些语义标签,会把它们用在哪些场景里呢?欢迎留言告诉我,我们一起讨论。
# 猜你喜欢
[<img src="https://static001.geekbang.org/resource/image/1a/08/1a49758821bdbdf6f0a8a1dc5bf39f08.jpg" alt="unpreview">](https://time.geekbang.org/course/intro/163?utm_term=zeusMTA7L&amp;utm_source=app&amp;utm_medium=chongxueqianduan&amp;utm_campaign=163-presell)

View File

@@ -0,0 +1,171 @@
<audio id="audio" title="HTML语言DTD到底是什么" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/84/f1/849b29c85a00830744e0573f5361c5f1.mp3"></audio>
你好我是winter。今天我们来聊一聊HTML语言。
我们平时写HTML语言都习惯把关注点放到各种标签上很少去深究它的语法。我想你应该会有模糊的感觉HTML这样的语言跟JavaScript这样的语言会有一些本质的不同。
实际上JavaScript语言我们把它称为“编程语言”它最大的特点是图灵完备的我们大致可以理解为“包含了表达一切逻辑的能力”。像HTML这样的语言我们称为“标记语言mark up language它是纯文本的一种升级“标记”一词的概念来自编辑审稿时使用不同颜色笔所做的“标记”。
在上世纪80年代“富文本”的概念在计算机领域的热门犹如如今的“AI”和“区块链”而Tim Berners-Lee当时去设计HTML也并非是凭空造出来他使用了当时已有的一种语言SGML。
SGML是一种古老的标记语言可以追溯到1969年IBM公司所使用的技术SGML十分复杂严格来说HTML是SGML中规定的一种格式但是实际的浏览器没有任何一个是通过SGML引擎来解析HTML的。
今天的HTML仍然有SGML的不少影子那么接下来我们就从SGML的一些特性来学习一下HTML。这里我最想讲的是SGML留给HTML的重要的遗产基本语法和DTD。
## 基本语法
首先HTML作为SGML的子集它遵循SGML的基本语法包括标签、转义等。
SGML还规定了一些特殊的节点类型在我们之前的DOM课程中已经讲过几种节点类型它们都有与之对应的HTML语法我们这里复习一下
<img src="https://static001.geekbang.org/resource/image/b6/bc/b6fdf08dbe47c837e274ff1bb6f630bc.jpg" alt="">
这里我们从语法的角度,再逐个具体了解一下。
### 标签语法
标签语法产生元素,我们从语法的角度讲,就用“标签”这个术语,我们从运行时的角度讲,就用“元素”这个术语。
HTML中用于描述一个元素的标签分为开始标签、结束标签和自闭合标签。开始标签和自闭合标签中又可以有属性。
<li>开始标签:`&lt;tagname&gt;`
<ul>
- 带属性的开始标签: `&lt;tagname attributename="attributevalue"&gt;`
HTML中开始标签的标签名称只能使用英文字母。
这里需要重点讲一讲属性语法,属性可以使用单引号、双引号或者完全不用引号,这三种情况下,需要转义的部分都不太一样。
属性中可以使用文本实体(后文会介绍)来做转义,属性中,一定需要转义的有下面几种。
- 无引号属性:`&lt;tab&gt;` `&lt;LF&gt;` `&lt;FF&gt;` `&lt;SPACE&gt;` `&amp;`五种字符。
- 单引号属性:`'` `&amp;`两种字符。
- 双引号属性:`"` `&amp;`两种字符。
一般来说,灵活运用属性的形式,是不太用到文本实体转义的。
### 文本语法
在HTML中规定了两种文本语法一种是普通的文本节点另一种是CDATA文本节点。
文本节点看似是普通的文本,但是,其中有两种字符是必须做转义的:`&lt;``&amp;`
如果我们从某处拷贝了一段文本,里面包含了大量的 `&lt;``&amp;`那么我们就有麻烦了这时候就轮到我们的CDATA节点出场了。
CDATA也是一种文本它存在的意义是语法上的意义在CDATA节点内不需要考虑多数的转义情况。
CDATA内只有字符组合`]]&gt;`需要处理这里不能使用转义只能拆成两个CDATA节点。
### 注释语法
HTML注释语法以`&lt;!--`开头,以`--&gt;`结尾,注释的内容非常自由,除了`--&gt;`都没有问题。
如果注释的内容一定要出现 `--&gt;`,我们可以拆成多个注释节点。
### DTD语法文档类型定义
SGML的DTD语法十分复杂但是对HTML来说其实DTD的选项是有限的浏览器在解析DTD时把它当做几种字符串之一关于DTD我在本篇文章的后面会详细讲解。
### ProcessingInstruction语法处理信息
ProcessingInstruction多数情况下是给机器看的。HTML中规定了可以有ProcessingInstruction但是并没有规定它的具体内容所以可以把它视为一种保留的扩展机制。对浏览器而言ProcessingInstruction 的作用类似于注释。
ProcessingInstruction 包含两个部分,紧挨着第一个问号后,空格前的部分被称为“目标”,这个目标一般表示处理 ProcessingInstruction 的程序名。
剩余部分是它的文本信息,没有任何格式上的约定,完全由文档编写者和处理程序的编写者约定。
## DTD
现在我们来讲一下DTDDTD的全称是Document Type Defination也就是文档类型定义。SGML用DTD来定义每一种文档类型HTML属于SGML在HTML5出现之前HTML都是使用符合SGML规定的DTD。
如果你是一个上个时代走过来的前端一定还记得HTML4.01有三种DTD。分别是严格模式、过渡模式和frameset模式。
```
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
```
严格模式的DTD规定了HTML4.01中需要的标签。
```
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
```
过渡模式的DTD除了html4.01,还包含了一些被贬斥的标签,这些标签已经不再推荐使用了,但是过渡模式中仍保留了它们。
```
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"&gt;
```
frameset结构的网页如今已经很少见到了它使用frameset标签把几个网页组合到一起。
众所周知HTML中允许一些标签不闭合的用法实际上这些都是符合SGML规定的并且在DTD中规定好了的。但是一些程序员喜欢严格遵守XML语法保证标签闭合性所以HTML4.01又规定了XHTML语法同样有三个版本
版本一
```
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
```
版本二
```
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
```
版本三
```
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"&gt;
```
其实你看看就知道这些复杂的DTD写法并没有什么实际作用浏览器根本不会用SGML引擎解析它们因此到了HTML5干脆放弃了SGML子集这项坚持规定了一个简单的大家都能记住的DTD
```
&lt;!DOCTYPE html&gt;
```
但是HTML5仍然保留了HTML语法和XHTML语法。
## 文本实体
不知道你注意到没有HTML4.01的DTD里包含了一个长得很像是URL的东西其实它是真的可以访问的——但是W3C警告说禁止任何浏览器在解析网页的时候访问这个URL不然W3C的服务器会被压垮。我相信很多好奇的前端工程师都把它下载下来打开过。
这是符合SGML规范的DTD我们前面讲过SGML的规范十分复杂所以这里我并不打算讲SGML其实我也不会但是这不妨碍我们了解一下DTD的内容。这个DTD规定了HTML包含了哪些标签、属性和文本实体。其中文本实体分布在三个文件中HTMLsymbol.ent HTMLspecial.ent和HTMLlat1.ent。
所谓文本实体定义就是类似以下的代码:
```
&amp;lt;
&amp;nbsp;
&amp;gt;
&amp;amp;
```
每一个文本实体由`&amp;`开头,由`;`结束,这属于基本语法的规定,文本实体可以用`#`后跟一个十进制数字表示字符Unicode值。除此之外这两个符号之间的内容则由DTD决定。
我这里数了一下HTML4.01的DTD中共规定了255个文本实体找出这些实体和它们对应的Unicode编码就作为本次课程的课后小问题吧。
## 总结
今天的课程中我们讲了HTML的语法HTML语法源自SGML我们首先介绍了基本语法包含了五种节点标签元素、文本、注释、文档类型定义DTD和处理信息ProcessingInstruction
之后我们又重点介绍了两部分内容DTD和文本实体。
DTD在HTML4.01和之前都非常的复杂到了HTML5抛弃了SGML兼容变成简单的`&lt;!DOCTYPE html&gt;`
文本实体是HTML转义的重要手段我们讲解了基本用法HTML4.01中规定的部分,就留给大家作为课后问题了。
今天的课后问题是HTML4.01的DTD中共规定了255个文本实体请你找出这些实体和它们对应的Unicode编码吧。

View File

@@ -0,0 +1,253 @@
<audio id="audio" title="HTML链接除了a标签还有哪些标签叫链接" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d7/83/d751a80af90bbeab661fac66f8f74383.mp3"></audio>
你好我是winter。
在前面的课程中我讲到了HTML的语义和元信息标签今天这一课我们来讲另一类HTML元素链接。
链接这种元素可以说是占据了整个互联网。也正是因为无处不在的超链接才让我们的万维网如此繁荣。没有了超链接的HTML最多可以称为富文本没法称作超文本hyper text
我想,作为互联网从业者,我们一定对链接都非常熟悉了。链接能够帮助我们从一个网页跳转到另一个网页。
不过除了肉眼可见的这些链接其实HTML里面还规定了一些不可见链接的类型这节课我就来给你介绍链接家族的全员让你对它们有一个完整的认识。
链接是HTML中的一种机制它是HTML文档和其它文档或者资源的连接关系在HTML中链接有两种类型。一种是超链接型标签一种是外部资源链接。
链接的家族中有a标签、area标签和link标签。今天我会逐一对它们进行介绍。
<img src="https://static001.geekbang.org/resource/image/ca/51/caab7832c425b3af2b3adae747e6f551.png" alt="">
## link 标签
提到链接我们都知道a标签可以成为超链接但是我们今天的内容要从一个大家不太熟悉的标签开始也就是link标签。
我们已经介绍过元信息类标签。实际上我们并没有介绍完全有些link标签也是元信息类标签的一种。
我们已经讲过HTML标准并没有规定浏览器如何使用元信息我们还讲到了元信息中有不少是被设计成“无需被浏览器识别而是专门用于搜索引擎看的”。
link标签也是元信息的一种在很多时候它也是不会对浏览器产生任何效果的这也是很多人会忽略link标签学习的原因。
link标签会生成一个链接它可能生成超链接也可能生成外部资源链接。
一些link标签会生成超链接这些超链接又不会像a标签那样显示在网页中。这就是超链接型的link标签。
这意味着多数浏览器中这些link标签不产生任何作用。但是这些link标签能够被搜索引擎和一些浏览器插件识别从而产生关键性作用。
比如到页面RSS的link标签能够被浏览器的RSS订阅插件识别提示用户当前页面是可以RSS订阅的。
另外一些link标签则会把外部的资源链接到文档中也就是说会实际下载这些资源并且做出一些处理比如我们常见的用link标签引入样式表。
除了元信息的用法之外多数外部资源型的link标签还能够被放在body中使用从而起到把外部资源链接进文档的作用。
link标签的链接类型主要通过rel属性来区分在本篇文章中我们提到xx型link即表示属性rel为xx的link其代码类似下面
```
&lt;link rel=&quot;xx&quot; ...&gt;
```
下面我们先来看看超链接型link标签。
## 超链接类link标签
超链接型link标签是一种被动型链接在用户不操作的情况下它们不会被主动下载。
link标签具有特定的rel属性会成为特定类型的link标签。产生超链接的link标签包括具有 rel=“canonical” 的link、具有 rel="alternate"的link、具有rel=“prev” rel="next"的link等等。
### canonical型link
这种link的代码写法是这样
```
&lt;link rel=&quot;canonical&quot; href=&quot;...&quot;&gt;
```
这个标签提示页面它的主URL在网站中常常有多个URL指向同一页面的情况搜索引擎访问这类页面时会去掉重复的页面这个link会提示搜索引擎保留哪一个URL。
## alternate型link
这种link的代码写法是这样
```
&lt;link rel=&quot;alternate&quot; href=&quot;...&quot;&gt;
```
这个标签提示页面它的变形形式这个所谓的变形可能是当前页面内容的不同格式、不同语言或者为不同的设备设计的版本这种link通常也是提供给搜索引擎来使用的。
alternate型的link的一个典型应用场景是页面提供rss订阅时可以用这样的link来引入
```
&lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot; title=&quot;RSS&quot; href=&quot;...&quot;&gt;
```
除了搜索引擎外很多浏览器插件都能识别这样的link。
### prev型link和next型link
在互联网应用中,很多网页都属于一个序列,比如分页浏览的场景,或者图片展示的场景,每个网页是序列中的一个项。
这种时候就适合使用prev和next型的link标签来告诉搜索引擎或者浏览器它的前一项和后一项这有助于页面的批量展示。
因为next型link告诉浏览器“这是很可能访问的下一个页面”HTML标准还建议对next型link做预处理在本课后面的内容我们会讲到预处理类的link。
### 其它超链接类的link
其它超链接类link标签都表示一个跟当前文档相关联的信息可以把这样的link标签视为一种带链接功能的meta标签。
- rel=“author” 链接到本页面的作者,一般是 mailto:协议
- rel=“help” 链接到本页面的帮助页
- rel=“license” 链接到本页面的版权信息页
- rel=“search” 链接到本页面的搜索页面(一般是站内提供搜索时使用)
到这里我们已经讲完了所有的超链接类的link标签用法了。接下来我们讲讲外部资源类link标签。
## 外部资源类link标签
外部资源型link标签会被主动下载并且根据rel类型做不同的处理。外部资源型的标签包括具有icon型的link、预处理类link、modulepreload型的link、stylesheet、pingback。下面我们来一一介绍它们。
### icon型link
这类链接表示页面的icon。多数浏览器会读取icon型link并且把页面的icon展示出来。
icon型link是唯一一个外部资源类的元信息link其它元信息类link都是超链接这意味着icon型link中的图标地址默认会被浏览器下载和使用。
如果没有指定这样的link多数浏览器会使用域名根目录下的favicon.ico即使它并不存在所以从性能的角度考虑建议一定要保证页面中有icon型的link。
只有icon型link有有效的sizes属性HTML标准允许一个页面出现多个icon型link并且用sizes指定它适合的icon尺寸。
### 预处理类link
我们都知道导航到一个网站需要经过dns查询域名、建立连接、传输数据、加载进内存和渲染等一系列的步骤。
预处理类link标签就是允许我们控制浏览器提前针对一些资源去做这些操作以提高性能当然如果你乱用的话性能反而更差
下面我来列一下这些link类型
- dns-prefetch型link 提前对一个域名做dns查询这样的link里面的href实际上只有域名有意义。
- preconnect型link 提前对一个服务器建立tcp连接。
- prefetch型link 提前取href指定的url的内容。
- preload型link 提前加载href指定的url。
- prerender型link 提前渲染href指定的url。
### modulepreload型的link
modulepreload型link的作用是预先加载一个JavaScript的模块。这可以保证JS模块不必等到执行时才加载。
这里的所谓加载是指完成下载并放入内存并不会执行对应的JavaScript。
```
&lt;link rel=&quot;modulepreload&quot; href=&quot;app.js&quot;&gt;
&lt;link rel=&quot;modulepreload&quot; href=&quot;helpers.js&quot;&gt;
&lt;link rel=&quot;modulepreload&quot; href=&quot;irc.js&quot;&gt;
&lt;link rel=&quot;modulepreload&quot; href=&quot;fog-machine.js&quot;&gt;
&lt;script type=&quot;module&quot; src=&quot;app.js&quot;&gt;
```
这个例子来自HTML标准我们假设app.js中有 import “irc” 和 import “fog-machine”, 而 irc.js中有 import “helpers”。这段代码使用moduleload型link来预加载了四个js模块。
尽管单独使用script标签引用app.js也可以正常工作但是我们通过加入对四个JS文件的link标签使得四个JS文件有机会被并行地下载这样提高了性能。
### stylesheet型link
样式表大概是所有人最熟悉的link标签用法了。它的样子是下面这样的。
```
&lt;link rel=&quot;stylesheet&quot; href=&quot;xxx.css&quot; type=&quot;text/css&quot;&gt;
```
基本用法是从一个CSS文件创建一个样式表。这里type属性可以没有如果有必须是"text/css"才会生效。
rel前可以加上alternate成为rel=“alternate stylesheet”此时必须再指定title属性。
这样可以为页面创建一份变体样式,一些浏览器,如 Firefox 3.0,支持从浏览器菜单中切换这些样式,当然了,大部分浏览器不支持这个功能,所以仅仅从语义的角度了解一下这种用法即可。
### pingback型link
这样的link表示本网页被引用时应该使用的pingback地址这个机制是一份独立的标准遵守pingback协议的网站在引用本页面时会向这个pingback url发送一个消息。
以上就是link标签的所有用法了。接下来我们来介绍一下最熟悉的 a 标签,当然了,也可能你学过了本节课以后,觉得自己其实也没那么熟悉。
## a 标签
a标签是“anchor”的缩写它是锚点的意思所谓锚点实际上也是一种比喻的用法古代船舶用锚来固定自己的位置避免停泊时被海浪冲走所以anchor标签的意思也是标识文档中的特定位置。
a标签其实同时充当了链接和目标点的角色当a标签有href属性时它是链接当它有name时它是链接的目标。
具有href的a标签跟一些link一样会产生超链接也就是在用户不操作的情况下它们不会被主动下载的被动型链接。
重点的内容是a标签也可以有rel属性我们来简单了解一下首先是跟link相同的一些rel包括下面的几种。
- alternate
- author
- help
- license
- next
- prev
- search
这些跟link语义完全一致不同的是a标签产生的链接是会实际显示在网页中的而link标签仅仅是元信息。
除了这些之外a标签独有的rel类型
- tag 表示本网页所属的标签;
- bookmark 到上级章节的链接。
a标签还有一些辅助的rel类型用于提示浏览器或者搜索引擎做一些处理
- nofollow 此链接不会被搜索引擎索引;
- noopener 此链接打开的网页无法使用opener来获得当前页面的窗口
- noreferrer 此链接打开的网页无法使用referrer来获得当前页面的url
- opener 打开的网页可以使用window.opener来访问当前页面的window对象这是a标签的默认行为。
a标签基本解决了在页面中插入文字型和整张图片超链接的需要但是如果我们想要在图片的某个区域产生超链接那么就要用到另一种标签了——area标签。
## area 标签
area标签与a标签非常相似不同的是它不是文本型的链接而是区域型的链接。
area标签支持的rel与a完全一样这里就不多说了。
area是整个html规则中唯一支持非矩形热区的标签它的shape属性支持三种类型。
- 圆形circle或者circcoords支持三个值分别表示中心点的x,y坐标和圆形半径r。
- 矩形rect或者rectanglecoords支持两个值分别表示两个对角顶点x1y1和x2y2。
- 多边形poly或者polygoncoords至少包括6个值表示多边形的各个顶点。
因为area设计的时间较早所以不支持含有各种曲线的路径但是它也是唯一一个支持了非矩形触发区域的元素所以对于一些效果而言area是必不可少的。
area必须跟img和map标签配合使用。使用示例如下例子来自html标准
```
&lt;p&gt;
Please select a shape:
&lt;img src=&quot;shapes.png&quot; usemap=&quot;#shapes&quot;
alt=&quot;Four shapes are available: a red hollow box, a green circle, a blue triangle, and a yellow four-pointed star.&quot;&gt;
&lt;map name=&quot;shapes&quot;&gt;
&lt;area shape=rect coords=&quot;50,50,100,100&quot;&gt; &lt;!-- the hole in the red box --&gt;
&lt;area shape=rect coords=&quot;25,25,125,125&quot; href=&quot;red.html&quot; alt=&quot;Red box.&quot;&gt;
&lt;area shape=circle coords=&quot;200,75,50&quot; href=&quot;green.html&quot; alt=&quot;Green circle.&quot;&gt;
&lt;area shape=poly coords=&quot;325,25,262,125,388,125&quot; href=&quot;blue.html&quot; alt=&quot;Blue triangle.&quot;&gt;
&lt;area shape=poly coords=&quot;450,25,435,60,400,75,435,90,450,125,465,90,500,75,465,60&quot;
href=&quot;yellow.html&quot; alt=&quot;Yellow star.&quot;&gt;
&lt;/map&gt;
&lt;/p&gt;
```
这个例子展示了在一张图片上画热区并且产生链接分别使用了矩形、圆形和多边形三种area。
## 结语
本节课我们介绍了几种链接类型。在HTML中链接有两种类型。一种是超链接型标签一种是外部资源链接。
我们逐次讲到了link标签、a标签和area标签link标签一般用于看不见的链接它可能产生超链接或者外部资源链接a和area一般用于页面上显示的链接它们只能产生超链接。
最后留给你一个思考问题你的工作中是使用过哪些类型的link标签的呢
# 猜你喜欢
[<img src="https://static001.geekbang.org/resource/image/1a/08/1a49758821bdbdf6f0a8a1dc5bf39f08.jpg" alt="unpreview">](https://time.geekbang.org/course/intro/163?utm_term=zeusMTA7L&amp;utm_source=app&amp;utm_medium=chongxueqianduan&amp;utm_campaign=163-presell)