mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-16 22:23:45 +08:00
del
This commit is contained in:
182
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS Flex排版:为什么垂直居中这么难?.md
Normal file
182
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS Flex排版:为什么垂直居中这么难?.md
Normal 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三大经典问题(简直易如反掌)。
|
||||
|
||||
垂直居中:
|
||||
|
||||
```
|
||||
<div id="parent">
|
||||
<div id="child">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
#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;来保证行位于容器中,元素位于行中。
|
||||
|
||||
两列等高:
|
||||
|
||||
```
|
||||
<div class="parent">
|
||||
<div class="child" style="height:300px;">
|
||||
</div>
|
||||
<div class="child">
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="parent">
|
||||
<div class="child" >
|
||||
</div>
|
||||
<div class="child" style="height:300px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
.parent {
|
||||
display:flex;
|
||||
width:300px;
|
||||
justify-content:center;
|
||||
align-content:center;
|
||||
align-items:stretch;
|
||||
}
|
||||
.child {
|
||||
width:100px;
|
||||
outline:solid 1px;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
思路是创建一个只有一行的flexbox,然后用stretch属性让每个元素高度都等于行高。
|
||||
|
||||
自适应宽:
|
||||
|
||||
```
|
||||
<div class="parent">
|
||||
<div class="child1">
|
||||
</div>
|
||||
<div class="child2">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
.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布局。
|
||||
|
||||
|
||||
226
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS 选择器:如何选中svg里的a元素?.md
Normal file
226
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS 选择器:如何选中svg里的a元素?.md
Normal 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选择器的一些机制。
|
||||
|
||||
**选择器的基本意义是:根据一些特征,选中元素树上的一批元素。**
|
||||
|
||||
我们把选择器的结构分一下类,那么由简单到复杂可以分成以下几种。
|
||||
|
||||
- 简单选择器:针对某一特征判断是否选中元素。
|
||||
- 复合选择器:连续写在一起的简单选择器,针对元素自身特征选择单个元素。
|
||||
- 复杂选择器:由“(空格)”“ >”“ ~”“ +”“ ||”等符号连接的复合选择器,根据父元素或者前序元素检查单个元素。
|
||||
- 选择器列表:由逗号分隔的复杂选择器,表示“或”的关系。
|
||||
|
||||
我们可以看到,选择器是由简单选择器逐级组合而成的结构,那么我们就来首先看一下简单选择器。
|
||||
|
||||
## 简单选择器
|
||||
|
||||
我们在前面说过,简单选择器是针对某一特征判断是否为选中元素。今天我会为你介绍一系列常见的简单选择器,我们把相似的简单选择器放在一起,这样更易于你去记忆。
|
||||
|
||||
<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,就必须用带命名空间的类型选择器。
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JS Bin</title>
|
||||
</head>
|
||||
<body>
|
||||
<svg width="100" height="28" viewBox="0 0 100 28" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<desc>Example link01 - a link on an ellipse
|
||||
</desc>
|
||||
<a xlink:href="http://www.w3.org">
|
||||
<text y="100%">name</text>
|
||||
</a>
|
||||
</svg>
|
||||
<br/>
|
||||
<a href="javascript:void 0;">name</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@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语法。
|
||||
|
||||
```
|
||||
<a class="a b c">xxx</a>
|
||||
.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(行为伪类除外),你可以把你的答案分享出来,我们一起来探讨吧。
|
||||
|
||||
|
||||
295
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS动画与交互:为什么动画要用贝塞尔曲线这么奇怪的东西?.md
Normal file
295
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS动画与交互:为什么动画要用贝塞尔曲线这么奇怪的东西?.md
Normal 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和P1,P1和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 < 8; i++) {
|
||||
// f(t)-x=0
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
derivative = sampleCurveDerivativeX(t2);
|
||||
// == 0, failure
|
||||
/* istanbul ignore if */
|
||||
if (Math.abs(derivative) < 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 > t0) {
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
if (x2 > 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一模一样的动画。
|
||||
|
||||
## 贝塞尔曲线拟合
|
||||
|
||||
理论上,贝塞尔曲线可以通过分段的方式拟合任意曲线,但是有一些特殊的曲线,是可以用贝塞尔曲线完美拟合的,比如抛物线。
|
||||
|
||||
这里我做了一个示例,用于模拟抛物线:
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Simulation</title>
|
||||
<style>
|
||||
.ball {
|
||||
width:10px;
|
||||
height:10px;
|
||||
background-color:black;
|
||||
border-radius:5px;
|
||||
position:absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
transform:translateY(180px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<label>运动时间:<input value="3.6" type="number" id="t" />s</label><br/>
|
||||
<label>初速度:<input value="-21" type="number" id="vy" /> px/s</label><br/>
|
||||
<label>水平速度:<input value="21" type="number" id="vx" /> px/s</label><br/>
|
||||
<label>重力:<input value="10" type="number" id="g" /> px/s²</label><br/>
|
||||
<button onclick="createBall()">来一个球</button>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
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来做一下对比,看看有哪些区别。
|
||||
|
||||
|
||||
137
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS小实验:动手做,用代码挖掘CSS属性.md
Normal file
137
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS小实验:动手做,用代码挖掘CSS属性.md
Normal file
File diff suppressed because one or more lines are too long
265
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS排版:从毕升开始,我们就开始用正常流了.md
Normal file
265
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS排版:从毕升开始,我们就开始用正常流了.md
Normal 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的块级元素容器,它的块级格式化上下文和外部的块级格式化上下文发生了融合,也就是说,如果不考虑盒模型相关的属性,这样的元素从排版的角度就好像根本不存在。
|
||||
|
||||
好了,到这里我们已经讲完了正常流的排版详细规则,但是理解规则仅仅是基础,我们还需要掌握一些技巧。
|
||||
|
||||
## 正常流的使用技巧
|
||||
|
||||
现在,我们就一起来动手用实际的例子来研究一下。我们今天来看看等分布局和自适应宽,从这两种经典布局问题入手,一起来探索一下正常流的使用技巧。
|
||||
|
||||
### 等分布局问题
|
||||
|
||||
横向等分布局是一个很常见的需求,按照一般的思路,我们可以使用百分比宽度来解决,我们参考以下代码:
|
||||
|
||||
```
|
||||
<div class="outer">
|
||||
<div class="inner"></div>
|
||||
<div class="inner"></div>
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
.inner {
|
||||
width:33.33%;
|
||||
height:300px;
|
||||
display:inline-block;
|
||||
outline:solid 1px blue;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
在这段HTML代码中,我们放了三个div,用CSS给它们指定了百分比宽度,并且指定为inline-block。
|
||||
|
||||
但是这段代码执行之后,效果跟我们预期不同,我们可以发现,每个div并非紧挨,中间有空白,这是因为我们为了代码格式加入的换行和空格被HTML当作空格文本,跟inline盒混排了的缘故。
|
||||
|
||||
解决方案是修改HTML代码,去掉空格和换行:
|
||||
|
||||
```
|
||||
<div class="outer"><div class="inner"></div><div class="inner"></div><div class="inner"></div></div>
|
||||
|
||||
```
|
||||
|
||||
但是这样做影响了源代码的可读性,一个变通的方案是,改变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-block,float也可以实现类似的效果,但是float元素只能做顶对齐,不如inline-block灵活。
|
||||
|
||||
### 自适应宽
|
||||
|
||||
我们再来说说自适应宽。在IE6统治浏览器市场的旧时代,自适应宽(一个元素固定宽度,另一个元素填满父容器剩余宽度)是个经典的布局问题,我们现在就看一下如何使用正常流来解决。
|
||||
|
||||
我们首先来看一下问题。
|
||||
|
||||
```
|
||||
<div class="outer">
|
||||
<div class="fixed"></div>
|
||||
<div class="auto"></div>
|
||||
</div>
|
||||
.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&utm_source=app&utm_medium=chongxueqianduan&utm_campaign=163-presell)
|
||||
182
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS渲染:CSS是如何绘制颜色的?.md
Normal file
182
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS渲染:CSS是如何绘制颜色的?.md
Normal 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颜色,因为它是一种语义化的颜色。当我们对一张图片改变色相时,人们感知到的是“图片的颜色变了”。这里先容我卖个关子,具体的例子待我们讲完了渐变再看。
|
||||
|
||||
### 其它颜色
|
||||
|
||||
接下来我们讲一讲RGBA,RGBA是代表Red(红色)、Green(绿色)、Blue(蓝色)和Alpha的色彩空间。RGBA颜色被用来表示带透明度的颜色,实际上,Alpha通道类似一种颜色值的保留字。在CSS中,Alpha通道被用于透明度,所以我们的颜色表示被称作 RGBA,而不是RGBO(Opacity)。
|
||||
|
||||
为了方便使用,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
|
||||
|
||||
我们组合一下,产生一个“真正的金色”的背景:
|
||||
|
||||
```
|
||||
<style>
|
||||
#grad1 {
|
||||
height: 200px;
|
||||
background: linear-gradient(45deg, gold 10%, yellow 50%, gold 90%);
|
||||
}
|
||||
</style>
|
||||
<div id="grad1"></div>
|
||||
|
||||
```
|
||||
|
||||
放射性渐变需要一个中心点和若干个颜色:
|
||||
|
||||
```
|
||||
radial-gradient(shape size at position, start-color, ..., last-color);
|
||||
|
||||
```
|
||||
|
||||
当我们应用的每一种颜色都是HSL颜色时,就产生了一些非常有趣的效果,比如,我们可以通过变量来调整一个按钮的风格:
|
||||
|
||||
```
|
||||
<style>
|
||||
.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 ;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="button orange">123</div>
|
||||
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
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,把这只雄性折衷鹦鹉变成跟雌性一样可爱的红色吗?
|
||||
|
||||
|
||||
300
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS语法:除了属性和选择器,你还需要知道这些带@的规则.md
Normal file
300
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS语法:除了属性和选择器,你还需要知道这些带@的规则.md
Normal 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 [ <url> | <string> ]
|
||||
[ supports( [ <supports-condition> | <declaration> ] ) ]?
|
||||
<media-query-list>? ;
|
||||
|
||||
```
|
||||
|
||||
通过代码,我们可以看出,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范围的关键字: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接受属性值的控制。
|
||||
|
||||
## 总结
|
||||
|
||||
在这一部分,我们介绍了CSS语法的总体结构,CSS的语法总体结构是由两种规则列表构成,一种是at 规则,另一种是普通规则。
|
||||
|
||||
在at规则中,我举了13个以上的例子,并逐个进行了简单的介绍。而在普通规则的部分,我介绍了选择器和声明区块是普通规则的主要组成部分。
|
||||
|
||||
并且,我给出了一个(不太严谨)的选择器语法结构,声明区块则由属性和值构成,这一部分我们重点介绍了函数。
|
||||
|
||||
从整体上去掌握内容,再去定位到单个细节,这对于我们学习CSS有非常重要的提示作用。
|
||||
|
||||
最后,给你留一个思考问题,CSS的函数有很多,本文也提到了不少,请你也一起查阅资料,试着总结一下,你能找到多少种CSS函数?
|
||||
|
||||
|
||||
372
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS选择器:伪元素是怎么回事儿?.md
Normal file
372
极客时间专栏/geek/重学前端/模块二:HTML和CSS/CSS选择器:伪元素是怎么回事儿?.md
Normal 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>
|
||||
- 无连接符号
|
||||
|
||||
第二优先级
|
||||
|
||||
- “空格”
|
||||
- “~”
|
||||
- “+”
|
||||
- “>”
|
||||
- “||”
|
||||
|
||||
第三优先级
|
||||
|
||||
- “,”
|
||||
|
||||
例如以下选择器:
|
||||
|
||||
```
|
||||
.c,.a>.b.d {
|
||||
/*......*/
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们应该理解为这样的结构。
|
||||
|
||||
<li>.c,.a>.b.d
|
||||
<ul>
|
||||
- .c
|
||||
<li>.a>.b.d
|
||||
<ul>
|
||||
- .a
|
||||
<li>.b.d
|
||||
<ul>
|
||||
- .b
|
||||
- .d
|
||||
|
||||
复合选择器表示简单选择器中“且”的关系,例如,例子中的“ .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的方式,来避免过于复杂的选择器结构,这样更有利于维护和性能。
|
||||
|
||||
空格和子代选择器通常用于组件化场景,当组件是独立开发时,很难完全避免class重名的情况,如果为组件的最外层容器元素设置一个特别的class名,生成CSS规则时,则全部使用后代或者子代选择器,这样可以有效避免CSS规则的命名污染问题。
|
||||
|
||||
逗号表示“或”的关系,实际上,可以把它理解为“两条内容一样的CSS规则”的一种简写。如我们开头的例子,可以理解成与下面的代码等效:
|
||||
|
||||
```
|
||||
.c {
|
||||
/*......*/
|
||||
}
|
||||
.a>.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”。
|
||||
|
||||
这个用法非常危险,因为它相当于一个新的优先级,而且此优先级会高于行内属性。
|
||||
|
||||
同一优先级的选择器遵循“后面的覆盖前面的”原则,我们可以看一个例子:
|
||||
|
||||
```
|
||||
<div id="my" class="x y">text<div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
.x {
|
||||
background-color:lightblue;
|
||||
}
|
||||
.y {
|
||||
background-color:lightgreen;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
调换“.x”和“.y”我们可以得到不同的显示效果。选择器的优先级是针对单条规则的,多条规则的选择器同时命中元素,优先级不会发生叠加。
|
||||
|
||||
```
|
||||
<div id="my" class="x y z">text<div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
.x {
|
||||
background-color:lightblue;
|
||||
}
|
||||
.z {
|
||||
background-color:lightblue;
|
||||
}
|
||||
.y {
|
||||
background-color:lightgreen;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
在这个例子中,“.x ”和“.z ”都指定了背景色为浅蓝色,但是因为“.y ”规则在最后,所以最终显示结果为浅绿色。另外一个需要注意的是,选择器的优先级是针对复杂选择器的优先级,选择器列表不会合并计算优先级。
|
||||
|
||||
我们看一个例子:
|
||||
|
||||
```
|
||||
<div id="my" class="x y z">text<div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
.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 是比较类似的伪元素**,其中一个表示元素的第一行,一个表示元素的第一个字母。
|
||||
|
||||
我们可以看一个示例:
|
||||
|
||||
```
|
||||
<p>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.</p>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
p::first-line {
|
||||
text-transform: uppercase
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这一段代码把段落的第一行字母变为大写。注意这里的第一行指的是排版后显示的第一行,跟HTML代码中的换行无关。
|
||||
|
||||
::first-letter 则指第一个字母。首字母变大并向左浮动是一个非常常见的排版方式。
|
||||
|
||||
```
|
||||
<p>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.</p>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
p::first-letter {
|
||||
text-transform: uppercase;
|
||||
font-size:2em;
|
||||
float:left;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
虽然听上去很简单,但是实际上,我们遇到的HTML结构要更为复杂,一旦元素中不是纯文本,规则就变得复杂了。
|
||||
|
||||
CSS标准规定了first-line必须出现在最内层的块级元素之内。因此,我们考虑以下代码。
|
||||
|
||||
```
|
||||
<div>
|
||||
<p id=a>First paragraph</p>
|
||||
<p>Second paragraph</p>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
div>p#a {
|
||||
color:green;
|
||||
}
|
||||
|
||||
div::first-line {
|
||||
color:blue;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这段代码最终结果第一行是蓝色,因为p是块级元素,所以伪元素出现在块级元素之内,所以内层的color覆盖了外层的color属性。
|
||||
|
||||
如果我们把p换成span,结果就是相反的。
|
||||
|
||||
```
|
||||
<div>
|
||||
<span id=a>First paragraph</span><br/>
|
||||
<span>Second paragraph</span>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
div>span#a {
|
||||
color:green;
|
||||
}
|
||||
|
||||
div::first-line {
|
||||
color:blue;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这段代码的最终结果是绿色,这说明伪元素在span之外。
|
||||
|
||||
::first-letter的行为又有所不同,它的位置在所有标签之内,我们把前面的代码换成::first-letter。
|
||||
|
||||
```
|
||||
<div>
|
||||
<span id=a>First paragraph</span><br/>
|
||||
<span>Second paragraph</span>
|
||||
</div>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
div>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属性才会生效,我们看下例子:
|
||||
|
||||
```
|
||||
<p class="special">I'm real element</p>
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
p.special::before {
|
||||
display: block;
|
||||
content: "pseudo! ";
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这里要注意一点,::before 和 ::after 还支持content为counter,如:
|
||||
|
||||
```
|
||||
<p class="special">I'm real element</p>
|
||||
p.special::before {
|
||||
display: block;
|
||||
content: counter(chapno, upper-roman) ". ";
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
这对于实现一些列表样式是非常有用的。
|
||||
|
||||
::before 和 ::after 中支持所有的CSS属性。实际开发中,这两个伪元素非常有用,有了这两个伪元素,一些修饰性元素,可以使用纯粹的CSS代码添加进去,这能够很好地保持HTML代码中的语义,既完成了显示效果,又不会让DOM中出现很多无语义的空元素。
|
||||
|
||||
## 结语
|
||||
|
||||
这一课,我们讲了CSS选择器的三种机制:选择器的组合、选择器优先级、以及伪元素。
|
||||
|
||||
在选择器组合这一部分,我们讲到了,选择器的连接方式像四则运算一样有优先级。
|
||||
|
||||
第一优先级是无连接符号;第二优先级是:“空格”“~”“+”“>”“||”;第三优先级是“,”。
|
||||
|
||||
然后我们又介绍了选择器优先级的计算方式。
|
||||
|
||||
最后我们介绍了伪元素,我们逐次讲解了:
|
||||
|
||||
- ::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&utm_source=app&utm_medium=chongxueqianduan&utm_campaign=163-presell)
|
||||
129
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML·ARIA:可访问性是只给盲人用的特性么?.md
Normal file
129
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML·ARIA:可访问性是只给盲人用的特性么?.md
Normal 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,我们来看一个例子:
|
||||
|
||||
```
|
||||
<span role="checkbox" aria-checked="false" tabindex="0" aria-labelledby="chk1-label">
|
||||
</span> <label id="chk1-label">Remember my preferences</label>
|
||||
|
||||
```
|
||||
|
||||
这里我们给一个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的继承关系用可视化的方式展现出来。
|
||||
|
||||
|
||||
171
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML元信息类标签:你知道head里一共能写哪几种标签吗?.md
Normal file
171
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML元信息类标签:你知道head里一共能写哪几种标签吗?.md
Normal 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则用于表示元信息的值。
|
||||
|
||||
它基本用法是下面这样的,你也可以自己动手尝试一下:
|
||||
|
||||
```
|
||||
<meta name=application-name content="lsForums">
|
||||
|
||||
```
|
||||
|
||||
这个标签表示页面所在的web-application,名为IsForums。
|
||||
|
||||
这里的name是一种比较自由的约定,HTTP标准规定了一些name作为大家使用的共识,也鼓励大家发明自己的name来使用。
|
||||
|
||||
除了基本用法,meta标签还有一些变体,主要用于简化书写方式或者声明自动化行为。下面我就挑几种重点的内容来分别讲解一下。
|
||||
|
||||
### 具有charset属性的meta
|
||||
|
||||
从HTML5开始,为了简化写法,meta标签新增了charset属性。添加了charset属性的meta标签无需再有name和content。
|
||||
|
||||
```
|
||||
<meta charset="UTF-8" >
|
||||
|
||||
```
|
||||
|
||||
charset型meta标签非常关键,它描述了HTML文档自身的编码形式。因此,我建议这个标签放在head的第一个。
|
||||
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
……
|
||||
|
||||
```
|
||||
|
||||
这样,浏览器读到这个标签之前,处理的所有字符都是ASCII字符,众所周知,ASCII字符是UTF-8和绝大多数字符编码的子集,所以,在读到meta之前,浏览器把文档理解多数编码格式都不会出错,这样可以最大限度地保证不出现乱码。
|
||||
|
||||
一般情况下,HTTP服务端会通过http头来指定正确的编码方式,但是有些特殊的情况如使用file协议打开一个HTML文件,则没有http头,这种时候,charset meta就非常重要了。
|
||||
|
||||
## 具有http-equiv属性的meta
|
||||
|
||||
具有http-equiv属性的meta标签,表示执行一个命令,这样的meta标签可以不需要name属性了。
|
||||
|
||||
例如,下面一段代码,相当于添加了content-type这个http头,并且指定了http编码方式。
|
||||
|
||||
```
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
|
||||
```
|
||||
|
||||
除了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。
|
||||
|
||||
例如:
|
||||
|
||||
```
|
||||
<meta name="viewport" content="width=500, initial-scale=1">
|
||||
|
||||
```
|
||||
|
||||
这里只指定了两个属性,宽度和缩放,实际上viewport能控制的更多,它能表示的全部属性如下:
|
||||
|
||||
- width:页面宽度,可以取值具体的数字,也可以是device-width,表示跟设备宽度相等。
|
||||
- height:页面高度,可以取值具体的数字,也可以是device-height,表示跟设备高度相等。
|
||||
- initial-scale:初始缩放比例。
|
||||
- minimum-scale:最小缩放比例。
|
||||
- maximum-scale:最大缩放比例。
|
||||
- user-scalable:是否允许用户缩放。
|
||||
|
||||
对于已经做好了移动端适配的网页,应该把用户缩放功能禁止掉,宽度设为设备宽度,一个标准的meta如下:
|
||||
|
||||
```
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
|
||||
|
||||
```
|
||||
|
||||
## 其它预定义的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标签的用法?欢迎留言告诉我。
|
||||
|
||||
|
||||
364
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML小实验:用代码分析HTML标准.md
Normal file
364
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML小实验:用代码分析HTML标准.md
Normal 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(".element"), e=>e.innerText);
|
||||
|
||||
```
|
||||
|
||||
这样我们就得到了所有元素的定义了,现在有107个元素。
|
||||
|
||||
不过,比较尴尬的是,这些文本中并不包含元素名,我们只好从id属性中获取,最后代码类似这样:
|
||||
|
||||
```
|
||||
var elementDefinations = Array.prototype.map.call(document.querySelectorAll(".element"), e => ({
|
||||
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标准中挖掘出来很多想要的信息,编写代码的过程,也是更深入理解标准的契机。
|
||||
|
||||
我们前面的课程中把元素分成了几类来讲解,但是这些分类只能大概地覆盖所有的标签,我设置课程的目标也是讲解标签背后的知识,而非每一种标签的细节。具体每一种标签的属性和细节,可以留给大家自己去整理。
|
||||
|
||||
这一节课的产出,则是“绝对完整的标签列表”,也是我学习和阅读标准的小技巧,通过代码我们可以从不同的侧面分析标准的内容,挖掘需要注意的点,这是一种非常好的学习方法。
|
||||
|
||||
|
||||
@@ -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标签的两种用法:
|
||||
|
||||
```
|
||||
|
||||
<script type="text/javascript">
|
||||
console.log("Hello world!");
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="my.js"></script>
|
||||
|
||||
|
||||
```
|
||||
|
||||
这个例子中,我们展示了两种script标签的写法,一种是直接把脚本代码写在script标签之间,另一种是把代码放到独立的js文件中,用src属性引入。
|
||||
|
||||
这两种写法是等效的。我想这种等效性可以帮助你理解替换型元素的“替换”是怎么一回事。
|
||||
|
||||
这里我们就可以回答标题中的问题了:凡是替换型元素,都是使用src属性来引用文件的,而我们之前的课程中已经讲过,链接型元素是使用href标签的。
|
||||
|
||||
虽然我不知道当初是怎么设计的,但是style标签并非替换型元素,不能使用src属性,这样,我们用link标签引入CSS文件,当然就是用href标签啦。
|
||||
|
||||
接下来我们再看看别的替换型元素,先来了解一下img标签。
|
||||
|
||||
## img
|
||||
|
||||
毫无疑问我们最熟悉的替换型标签就是img标签了,几乎每个前端都会日常使用img标签。
|
||||
|
||||
img标签的作用是引入一张图片。这个标签是没有办法像script标签那样作为非替换型标签来使用的,它必须有src属性才有意义。
|
||||
|
||||
如果一定不想要引入独立文件,可以使用data uri,我们来看个实际的例子:
|
||||
|
||||
```
|
||||
<img src='data:image/svg+xml;charset=utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg"><rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/></svg>'/>
|
||||
|
||||
```
|
||||
|
||||
这个例子中我们使用了data uri作为图片的src,这样,并没有产生独立的文件,客观上做到了和内联相同的结果,这是一个常用的技巧。
|
||||
|
||||
img标签可以使用width和height指定宽度和高度。也可以只指定其中之一。我们看个例子:
|
||||
|
||||
```
|
||||
<img src='data:image/svg+xml;charset=utf8,<svg width="600" height="400" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"><ellipse cx="300" cy="150" rx="200" ry="80"
|
||||
style="fill:rgb(200,100,50);
|
||||
stroke:rgb(0,0,100);stroke-width:2"/></svg>' width="100"/>
|
||||
|
||||
```
|
||||
|
||||
这个例子中,为了方便你理解,我们把图片换成了椭圆,我们可以看到,当我们指定了宽度后,图片被**等比例缩放了**。这个特性非常重要,适用于那种我们既要限制图片尺寸,又要保持图片比例的场景。
|
||||
|
||||
如果从性能的角度考虑,建议你同时给出图片的宽高,因为替换型元素加载完文件后,如果尺寸发生变换,会触发重排版(这个概念我们在浏览器原理部分已经讲过,可以复习一下)。
|
||||
|
||||
此处要重点提到一个属性,alt属性,这个属性很难被普通用户感知,对于视障用户非常重要,可以毫不夸张地讲,给img加上alt属性,已经做完了可访问性的一半。
|
||||
|
||||
img标签还有一组重要的属性,那就是srcset和sizes,它们是src属性的升级版(所以我们前面讲img标签必须有src属性,这是不严谨的说法)。
|
||||
|
||||
这两个属性的作用是在不同的屏幕大小和特性下,使用不同的图片源。下面一个例子也来自MDN,它展示了srcset和sizes的用法
|
||||
|
||||
```
|
||||
<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">
|
||||
|
||||
```
|
||||
|
||||
srcset提供了根据屏幕条件选取图片的能力,但是其实更好的做法,是使用picture元素。
|
||||
|
||||
## picture
|
||||
|
||||
picture元素可以根据屏幕的条件为其中的img元素提供不同的源,它的基本用法如下:
|
||||
|
||||
```
|
||||
<picture>
|
||||
<source srcset="image-wide.png" media="(min-width: 600px)">
|
||||
<img src="image-narrow.png">
|
||||
</picture>
|
||||
|
||||
```
|
||||
|
||||
picture元素的设计跟audio和video保持了一致(稍后我会为你讲解这两个元素),它跟img搭配srcset和sizes不同,它使用source元素来指定图片源,并且支持多个。
|
||||
|
||||
这里的media属性是media query,跟CSS的@media规则一致。
|
||||
|
||||
## video
|
||||
|
||||
在HTML5早期的设计中,video标签跟img标签类似,也是使用src属性来引入源文件的,不过,我想应该是考虑到了各家浏览器支持的视频格式不同,现在的video标签跟picture元素一样,也是提倡使用source的。
|
||||
|
||||
下面例子是一个古典的video用法:
|
||||
|
||||
```
|
||||
<video controls="controls" src="movie.ogg">
|
||||
</video>
|
||||
|
||||
```
|
||||
|
||||
这个例子中的代码用src来指定视频的源文件。但是因为一些历史原因,浏览器对视频的编码格式兼容问题分成了几个派系,这样,对于一些兼容性要求高的网站,我们使用单一的视频格式是不合适的。
|
||||
|
||||
现在的video标签可以使用source标签来指定接入多个视频源。
|
||||
|
||||
```
|
||||
<video controls="controls" >
|
||||
<source src="movie.webm" type="video/webm" >
|
||||
<source src="movie.ogg" type="video/ogg" >
|
||||
<source src="movie.mp4" type="video/mp4">
|
||||
You browser does not support video.
|
||||
</video>
|
||||
|
||||
```
|
||||
|
||||
从这个例子中,我们可以看到,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元素来指定源文件。我们看一下例子:
|
||||
|
||||
```
|
||||
<audio controls>
|
||||
<source src="song.mp3" type="audio/mpeg">
|
||||
<source src="song.ogg" type="audio/ogg">
|
||||
<p>You browser does not support audio.</p>
|
||||
</audio>
|
||||
|
||||
```
|
||||
|
||||
但比起video,audio元素的历史问题并不严重,所以使用src也是没有问题的。
|
||||
|
||||
## iframe
|
||||
|
||||
最后我们来讲一下iframe,这个标签能够嵌入一个完整的网页。
|
||||
|
||||
不过,在移动端,iframe受到了相当多的限制,它无法指定大小,里面的内容会被完全平铺到父级页面上。
|
||||
|
||||
同时很多网页也会通过http协议头禁止自己被放入iframe中。
|
||||
|
||||
iframe标签也是各种安全问题的重灾区。opener、window.name、甚至css的opacity都是黑客可以利用的漏洞。
|
||||
|
||||
因此,在2019年,当下这个时间点,任何情况下我都不推荐在实际开发中用以前的iframe。
|
||||
|
||||
当然,不推荐使用是一回事,因为没人能保证不遇到历史代码,我们还是应该了解一下iframe的基本用法:
|
||||
|
||||
```
|
||||
<iframe src="http://time.geekbang.org"></iframe>
|
||||
|
||||
```
|
||||
|
||||
这个例子展示了古典的iframe用法。
|
||||
|
||||
在新标准中,为iframe加入了sandbox模式和srcdoc属性,这样,给iframe带来了一定的新场景。我们来看看例子:
|
||||
|
||||
```
|
||||
<iframe sandbox srcdoc="<p>Yeah, you can see it <a href="/gallery?mode=cover&amp;amp;page=1">in my gallery</a>."></iframe>
|
||||
|
||||
```
|
||||
|
||||
这个例子中,使用srcdoc属性创建了一个新的文档,嵌入在iframe中展示,并且使用了sandbox来隔离。
|
||||
|
||||
这样,这个iframe就不涉及任何跨域问题了。
|
||||
|
||||
## 总结
|
||||
|
||||
这节课,我们又认识了一组HTML元素:替换型元素。它们的特点是,引入一个外部资源来进入页面,替换掉自身的位置。
|
||||
|
||||
我们通过对script、img、picture、audio、video、iframe几个标签的讲解,了解了不同的资源引入方式:
|
||||
|
||||
- src属性;
|
||||
- srcset属性;
|
||||
- source标签;
|
||||
- srcdoc属性。
|
||||
|
||||
这中间,我们也介绍了一些小技巧,比如src属性的好朋友:data uri,这在实际开发中非常有用。
|
||||
|
||||
最后,留给你一个小问题,请查资料总结一下,在多数现代浏览器兼容的范围内,src属性支持哪些协议的uri(如http和我们提到的data)。
|
||||
|
||||
|
||||
303
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语义:div和span不是够用了吗?.md
Normal file
303
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语义:div和span不是够用了吗?.md
Normal 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表示重音:
|
||||
|
||||
```
|
||||
今天我吃了一个<em>苹果</em>。
|
||||
今天我吃了<em>一个</em>苹果。
|
||||
|
||||
```
|
||||
|
||||
通过em标签,我们可以消除这样的歧义。
|
||||
|
||||
一些文章常常会拿em和strong做对比,实际上,我们只要理解了em的真正意思,它和strong可谓天差地别,并没有任何混淆的可能。
|
||||
|
||||
## 作为标题摘要的语义类标签
|
||||
|
||||
介绍完自然语言的语义场景后,我想介绍的另一个语义重要使用场景,就是文章的结构。中国古代小说就形成了“章-回”的概念,西方的戏剧也有幕的区分,所以人类的自然语言作品也是如出一辙。
|
||||
|
||||
HTML也应该支持这样的需求。HTML语义标签中,有不少是用于支持这样的结构的标签。
|
||||
|
||||
语义化的HTML能够支持自动生成目录结构,HTML标准中还专门规定了生成目录结构的算法,即使我们并不打算深入实践语义,也应该尽量在大的层面上保证这些元素的语义化使用。
|
||||
|
||||
首先我们需要形成一个概念,一篇文档会有一个树形的目录结构,它由各个级别的标题组成。这个树形结构可能不会跟HTML元素的嵌套关系一致。
|
||||
|
||||
```
|
||||
例如:
|
||||
|
||||
<h1>HTML语义</h1>
|
||||
<p>balah balah balah balah</p>
|
||||
<h2>弱语义</h2>
|
||||
<p>balah balah</p>
|
||||
<h2>结构性元素</h2>
|
||||
<p>balah balah</p>
|
||||
......
|
||||
|
||||
```
|
||||
|
||||
这段HTML几乎是平铺的元素,但是它的标题结构是:
|
||||
|
||||
<li>HTML语义
|
||||
<ul>
|
||||
- 弱语义
|
||||
- 结构性元素
|
||||
- ……
|
||||
|
||||
h1-h6是最基本的标题,它们表示了文章中不同层级的标题。有些时候,我们会有副标题,为了避免副标题产生额外的一个层级,我们使用hgroup标签。
|
||||
|
||||
我们来看下有/无hgroup的对比:
|
||||
|
||||
```
|
||||
<h1>JavaScript对象</h1>
|
||||
<h2>我们需要模拟类吗?</h2>
|
||||
<p>balah balah</p>
|
||||
......
|
||||
|
||||
```
|
||||
|
||||
此段生成以下标题结构:
|
||||
|
||||
<li>JavaScript对象
|
||||
<ul>
|
||||
- 我们需要模拟类吗?
|
||||
- …
|
||||
|
||||
```
|
||||
<hgroup>
|
||||
<h1>JavaScript对象</h1>
|
||||
<h2>我们需要模拟类吗?</h2>
|
||||
</hgroup>
|
||||
<p>balah balah</p>
|
||||
......
|
||||
|
||||
```
|
||||
|
||||
这一段生成以下标题结构:
|
||||
|
||||
<li>JavaScript对象——我们需要模拟类吗?
|
||||
<ul>
|
||||
- …
|
||||
|
||||
我们通过两个效果的对比就可以知道,在hgroup中的h1-h6被视为同一标题的不同组成部分。
|
||||
|
||||
从HTML 5开始,我们有了section标签,这个标签可不仅仅是一个“有语义的div”,它会改变h1-h6的语义。section的嵌套会使得其中的h1-h6下降一级,因此,在HTML5以后,我们只需要section和h1就足以形成文档的树形结构:
|
||||
|
||||
```
|
||||
<section>
|
||||
<h1>HTML语义</h1>
|
||||
<p>balah balah balah balah</p>
|
||||
<section>
|
||||
<h1>弱语义</h1>
|
||||
<p>balah balah</p>
|
||||
</section>
|
||||
<section>
|
||||
<h1>结构性元素</h1>
|
||||
<p>balah balah</p>
|
||||
</section>
|
||||
......
|
||||
</section>
|
||||
|
||||
```
|
||||
|
||||
这段代码同样会形成前面例子的标题结构:
|
||||
|
||||
<li>HTML语义
|
||||
<ul>
|
||||
- 弱语义
|
||||
- 结构性元素
|
||||
- ……
|
||||
|
||||
## 作为整体结构的语义类标签
|
||||
|
||||
我们想介绍的最后一个场景是,随着越来越多的浏览器推出“阅读模式”,以及各种非浏览器终端的出现,语义化的HTML适合机器阅读的特性变得越来越重要。
|
||||
|
||||
应用了语义化结构的页面,可以明确地提示出页面信息的主次关系,它能让浏览器很好地支持“阅读视图功能”,还可以让搜索引擎的命中率提升,同时,它也对视障用户的读屏软件更友好。
|
||||
|
||||
我们正确使用整体结构类的语义标签,可以让页面对机器更友好。比如,这里一个典型的body类似这样:
|
||||
|
||||
```
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
……
|
||||
</nav>
|
||||
</header>
|
||||
<aside>
|
||||
<nav>
|
||||
……
|
||||
</nav>
|
||||
</aside>
|
||||
<section>……</section>
|
||||
<section>……</section>
|
||||
<section>……</section>
|
||||
<footer>
|
||||
<address>……</address>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
```
|
||||
|
||||
在body下面,有一个header,header里面是一个nav,跟header同级的有一个aside,aside里面也有一个nav。接下来是文章的整体,也就是一个一个的section。section里面可能还有嵌套,但是我们就不管了,最后是一个footer,这个footer里面可能有address这样的内容。
|
||||
|
||||
除此之外,还有article,article是一种特别的结构,它表示具有一定独立性质的文章。所以,article和body具有相似的结构,同时,一个HTML页面中,可能有多个article存在。
|
||||
|
||||
一个典型的场景是多篇新闻展示在同一个新闻专题页面中,这种类似报纸的多文章结构适合用article来组织。
|
||||
|
||||
```
|
||||
<body>
|
||||
<header>……</header>
|
||||
<article>
|
||||
<header>……</header>
|
||||
<section>……</section>
|
||||
<section>……</section>
|
||||
<section>……</section>
|
||||
<footer>……</footer>
|
||||
</article>
|
||||
<article>
|
||||
……
|
||||
</article>
|
||||
<article>
|
||||
……
|
||||
</article>
|
||||
<footer>
|
||||
<address></address>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
```
|
||||
|
||||
body里面有自己的header和footer,然后里面是竖篇的article,每一个article里面都有自己的header、section、footer。这是一个典型的多文章结构。
|
||||
|
||||
在这个结构里,我们看到了一些新标签,我也来逐个介绍一下。
|
||||
|
||||
- header,如其名,通常出现在前部,表示导航或者介绍性的内容。
|
||||
- footer,通常出现在尾部,包含一些作者信息、相关链接、版权信息等。
|
||||
|
||||
header和footer一般都是放在article或者body的直接子元素,但是标准中并没有明确规定,footer也可以和aside,nav,section相关联(header不存在关联问题)。
|
||||
|
||||
- aside表示跟文章主体不那么相关的部分,它可能包含导航、广告等工具性质的内容。
|
||||
|
||||
aside很容易被理解为侧边栏,实际上二者是包含关系,侧边栏是aside,aside不一定是侧边栏。
|
||||
|
||||
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&utm_source=app&utm_medium=chongxueqianduan&utm_campaign=163-presell)
|
||||
279
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语义:如何运用语义类标签来呈现Wiki网页?.md
Normal file
279
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语义:如何运用语义类标签来呈现Wiki网页?.md
Normal 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,所以代码可能是类似这样的:
|
||||
|
||||
```
|
||||
<hgroup>
|
||||
<h1>World Wide Web </h1>
|
||||
<h2>From Wikipedia, the free encyclopedia</h2>
|
||||
</hgroup>
|
||||
|
||||
```
|
||||
|
||||
## abbr
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/13/72/139b1603d3851b11e9ee4ed955aec972.png" alt="">
|
||||
|
||||
abbr标签表示缩写。考虑到WWW是World Wide Web的缩写,所以文中所有出现的WWW,都应该使用abbr标签。
|
||||
|
||||
```
|
||||
<abbr title="World Wide Web">WWW</abbr>.
|
||||
|
||||
```
|
||||
|
||||
## 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标签。
|
||||
|
||||
```
|
||||
<p>
|
||||
A global map of the web index for countries in 2014
|
||||
<strong>The World Wide Web (WWW)</strong>, also called <strong>the Web</strong>,
|
||||
......
|
||||
|
||||
```
|
||||
|
||||
## 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标签。
|
||||
|
||||
```
|
||||
<cite>"What is the difference between the Web and the Internet?"</cite>. 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标签:
|
||||
|
||||
```
|
||||
<cite>"What is the difference between the Web and the Internet?"</cite>. W3C Help and FAQ. W3C. 2009. Archived from the original on <time datetime="2015-07-09">9 July 2015</time>. Retrieved <time datetime="2015-07-16">16 July 2015</time>.
|
||||
|
||||
```
|
||||
|
||||
## figure, figcaption
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6d/72/6d473b6fb734ea85a8cc209bc1716b72.png" alt="">
|
||||
|
||||
我们注意一下文章的右侧,出现了几张图片,这种出现在文中的图片,不仅仅是一个img标签,它和下面的文字组成了一个figure的语法现象,figure也是我们的一种标签(用于表示与主文章相关的图像、照片等流内容)。
|
||||
|
||||
```
|
||||
<figure>
|
||||
<img src="https://.....440px-NeXTcube_first_webserver.JPG"/>
|
||||
<figcaption>The NeXT Computer used by Tim Berners-Lee at CERN.</figcaption>
|
||||
</figure>
|
||||
|
||||
```
|
||||
|
||||
这种插入文章中的内容,不仅限图片,代码、表格等,只要是具有一定自包含性(类似独立句子)的内容,都可以用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 <dfn>Internet</dfn> is a global system of interconnected computer networks.
|
||||
In contrast, the <dfn>World Wide Web</dfn> 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结构。
|
||||
|
||||
```
|
||||
<nav>
|
||||
<h2>Contents</h2>
|
||||
<ol>
|
||||
<li><a href="...">History</a></li>
|
||||
<li><a href="...">Function</a>
|
||||
<ol>
|
||||
<li><a href="...">Linking</a></li>
|
||||
<li><a href="...">Dynamic updates of web pages</a></li>
|
||||
...
|
||||
</ol>
|
||||
</li>
|
||||
...
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
```
|
||||
|
||||
我们这里必须要指出,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标签:
|
||||
|
||||
```
|
||||
<pre><samp>
|
||||
GET /home.html HTTP/1.1
|
||||
Host: www.example.org
|
||||
</samp></pre>
|
||||
|
||||
```
|
||||
|
||||
接下来Wiki中的内容出现了一段HTML代码,我们同样不希望浏览器做自动换行。
|
||||
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<title>Example.org – The World Wide Web</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>The World Wide Web, abbreviated as WWW and commonly known ...</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
因为同时是代码,我们还需要加上code标签。最后的代码是pre标签包裹了code标签,code标签包裹了HTML代码。
|
||||
|
||||
```
|
||||
<pre><code>
|
||||
&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,比如 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&utm_source=app&utm_medium=chongxueqianduan&utm_campaign=163-presell)
|
||||
171
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语言:DTD到底是什么?.md
Normal file
171
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML语言:DTD到底是什么?.md
Normal 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>开始标签:`<tagname>`
|
||||
<ul>
|
||||
- 带属性的开始标签: `<tagname attributename="attributevalue">`
|
||||
|
||||
HTML中开始标签的标签名称只能使用英文字母。
|
||||
|
||||
这里需要重点讲一讲属性语法,属性可以使用单引号、双引号或者完全不用引号,这三种情况下,需要转义的部分都不太一样。
|
||||
|
||||
属性中可以使用文本实体(后文会介绍)来做转义,属性中,一定需要转义的有下面几种。
|
||||
|
||||
- 无引号属性:`<tab>` `<LF>` `<FF>` `<SPACE>` `&`五种字符。
|
||||
- 单引号属性:`'` `&`两种字符。
|
||||
- 双引号属性:`"` `&`两种字符。
|
||||
|
||||
一般来说,灵活运用属性的形式,是不太用到文本实体转义的。
|
||||
|
||||
### 文本语法
|
||||
|
||||
在HTML中,规定了两种文本语法,一种是普通的文本节点,另一种是CDATA文本节点。
|
||||
|
||||
文本节点看似是普通的文本,但是,其中有两种字符是必须做转义的:`<` 和 `&`。
|
||||
|
||||
如果我们从某处拷贝了一段文本,里面包含了大量的 `<` 和 `&`,那么我们就有麻烦了,这时候,就轮到我们的CDATA节点出场了。
|
||||
|
||||
CDATA也是一种文本,它存在的意义是语法上的意义:在CDATA节点内,不需要考虑多数的转义情况。
|
||||
|
||||
CDATA内,只有字符组合`]]>`需要处理,这里不能使用转义,只能拆成两个CDATA节点。
|
||||
|
||||
### 注释语法
|
||||
|
||||
HTML注释语法以`<!--`开头,以`-->`结尾,注释的内容非常自由,除了`-->`都没有问题。
|
||||
|
||||
如果注释的内容一定要出现 `-->`,我们可以拆成多个注释节点。
|
||||
|
||||
### DTD语法(文档类型定义)
|
||||
|
||||
SGML的DTD语法十分复杂,但是对HTML来说,其实DTD的选项是有限的,浏览器在解析DTD时,把它当做几种字符串之一,关于DTD,我在本篇文章的后面会详细讲解。
|
||||
|
||||
### ProcessingInstruction语法(处理信息)
|
||||
|
||||
ProcessingInstruction多数情况下,是给机器看的。HTML中规定了可以有ProcessingInstruction,但是并没有规定它的具体内容,所以可以把它视为一种保留的扩展机制。对浏览器而言,ProcessingInstruction 的作用类似于注释。
|
||||
|
||||
ProcessingInstruction 包含两个部分,紧挨着第一个问号后,空格前的部分被称为“目标”,这个目标一般表示处理 ProcessingInstruction 的程序名。
|
||||
|
||||
剩余部分是它的文本信息,没有任何格式上的约定,完全由文档编写者和处理程序的编写者约定。
|
||||
|
||||
## DTD
|
||||
|
||||
现在我们来讲一下DTD,DTD的全称是Document Type Defination,也就是文档类型定义。SGML用DTD来定义每一种文档类型,HTML属于SGML,在HTML5出现之前,HTML都是使用符合SGML规定的DTD。
|
||||
|
||||
如果你是一个上个时代走过来的前端,一定还记得HTML4.01有三种DTD。分别是严格模式、过渡模式和frameset模式。
|
||||
|
||||
```
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
```
|
||||
|
||||
严格模式的DTD规定了HTML4.01中需要的标签。
|
||||
|
||||
```
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
```
|
||||
|
||||
过渡模式的DTD除了html4.01,还包含了一些被贬斥的标签,这些标签已经不再推荐使用了,但是过渡模式中仍保留了它们。
|
||||
|
||||
```
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
|
||||
|
||||
```
|
||||
|
||||
frameset结构的网页如今已经很少见到了,它使用frameset标签把几个网页组合到一起。
|
||||
|
||||
众所周知,HTML中允许一些标签不闭合的用法,实际上这些都是符合SGML规定的,并且在DTD中规定好了的。但是,一些程序员喜欢严格遵守XML语法,保证标签闭合性,所以,HTML4.01又规定了XHTML语法,同样有三个版本:
|
||||
|
||||
版本一
|
||||
|
||||
```
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
```
|
||||
|
||||
版本二
|
||||
|
||||
```
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
|
||||
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
```
|
||||
|
||||
版本三
|
||||
|
||||
```
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
||||
|
||||
```
|
||||
|
||||
其实你看看就知道,这些复杂的DTD写法并没有什么实际作用(浏览器根本不会用SGML引擎解析它们),因此,到了HTML5,干脆放弃了SGML子集这项坚持,规定了一个简单的,大家都能记住的DTD:
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
|
||||
```
|
||||
|
||||
但是,HTML5仍然保留了HTML语法和XHTML语法。
|
||||
|
||||
## 文本实体
|
||||
|
||||
不知道你注意到没有,HTML4.01的DTD里包含了一个长得很像是URL的东西,其实它是真的可以访问的——但是W3C警告说,禁止任何浏览器在解析网页的时候访问这个URL,不然W3C的服务器会被压垮。我相信很多好奇的前端工程师都把它下载下来打开过。
|
||||
|
||||
这是符合SGML规范的DTD,我们前面讲过,SGML的规范十分复杂,所以这里我并不打算讲SGML(其实我也不会),但是这不妨碍我们了解一下DTD的内容。这个DTD规定了HTML包含了哪些标签、属性和文本实体。其中文本实体分布在三个文件中:HTMLsymbol.ent HTMLspecial.ent和HTMLlat1.ent。
|
||||
|
||||
所谓文本实体定义就是类似以下的代码:
|
||||
|
||||
```
|
||||
&lt;
|
||||
&nbsp;
|
||||
&gt;
|
||||
&amp;
|
||||
|
||||
```
|
||||
|
||||
每一个文本实体由`&`开头,由`;`结束,这属于基本语法的规定,文本实体可以用`#`后跟一个十进制数字,表示字符Unicode值。除此之外这两个符号之间的内容,则由DTD决定。
|
||||
|
||||
我这里数了一下,HTML4.01的DTD中,共规定了255个文本实体,找出这些实体和它们对应的Unicode编码,就作为本次课程的课后小问题吧。
|
||||
|
||||
## 总结
|
||||
|
||||
今天的课程中我们讲了HTML的语法,HTML语法源自SGML,我们首先介绍了基本语法,包含了五种节点:标签(元素)、文本、注释、文档类型定义(DTD)和处理信息(ProcessingInstruction)。
|
||||
|
||||
之后我们又重点介绍了两部分内容:DTD和文本实体。
|
||||
|
||||
DTD在HTML4.01和之前都非常的复杂,到了HTML5,抛弃了SGML兼容,变成简单的`<!DOCTYPE html>`。
|
||||
|
||||
文本实体是HTML转义的重要手段,我们讲解了基本用法,HTML4.01中规定的部分,就留给大家作为课后问题了。
|
||||
|
||||
今天的课后问题是:HTML4.01的DTD中,共规定了255个文本实体,请你找出这些实体和它们对应的Unicode编码吧。
|
||||
|
||||
|
||||
253
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML链接:除了a标签,还有哪些标签叫链接?.md
Normal file
253
极客时间专栏/geek/重学前端/模块二:HTML和CSS/HTML链接:除了a标签,还有哪些标签叫链接?.md
Normal 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,其代码类似下面:
|
||||
|
||||
```
|
||||
<link rel="xx" ...>
|
||||
|
||||
```
|
||||
|
||||
下面我们先来看看超链接型link标签。
|
||||
|
||||
## 超链接类link标签
|
||||
|
||||
超链接型link标签是一种被动型链接,在用户不操作的情况下,它们不会被主动下载。
|
||||
|
||||
link标签具有特定的rel属性,会成为特定类型的link标签。产生超链接的link标签包括:具有 rel=“canonical” 的link、具有 rel="alternate"的link、具有rel=“prev” rel="next"的link等等。
|
||||
|
||||
### canonical型link
|
||||
|
||||
这种link的代码写法是这样:
|
||||
|
||||
```
|
||||
<link rel="canonical" href="...">
|
||||
|
||||
```
|
||||
|
||||
这个标签提示页面它的主URL,在网站中常常有多个URL指向同一页面的情况,搜索引擎访问这类页面时会去掉重复的页面,这个link会提示搜索引擎保留哪一个URL。
|
||||
|
||||
## alternate型link
|
||||
|
||||
这种link的代码写法是这样:
|
||||
|
||||
```
|
||||
<link rel="alternate" href="...">
|
||||
|
||||
```
|
||||
|
||||
这个标签提示页面它的变形形式,这个所谓的变形可能是当前页面内容的不同格式、不同语言或者为不同的设备设计的版本,这种link通常也是提供给搜索引擎来使用的。
|
||||
|
||||
alternate型的link的一个典型应用场景是,页面提供rss订阅时,可以用这样的link来引入:
|
||||
|
||||
```
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="...">
|
||||
|
||||
```
|
||||
|
||||
除了搜索引擎外,很多浏览器插件都能识别这样的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。
|
||||
|
||||
```
|
||||
<link rel="modulepreload" href="app.js">
|
||||
<link rel="modulepreload" href="helpers.js">
|
||||
<link rel="modulepreload" href="irc.js">
|
||||
<link rel="modulepreload" href="fog-machine.js">
|
||||
<script type="module" src="app.js">
|
||||
|
||||
```
|
||||
|
||||
这个例子来自HTML标准,我们假设app.js中有 import “irc” 和 import “fog-machine”, 而 irc.js中有 import “helpers”。这段代码使用moduleload型link来预加载了四个js模块。
|
||||
|
||||
尽管,单独使用script标签引用app.js也可以正常工作,但是我们通过加入对四个JS文件的link标签,使得四个JS文件有机会被并行地下载,这样提高了性能。
|
||||
|
||||
### stylesheet型link
|
||||
|
||||
样式表大概是所有人最熟悉的link标签用法了。它的样子是下面这样的。
|
||||
|
||||
```
|
||||
<link rel="stylesheet" href="xxx.css" type="text/css">
|
||||
|
||||
```
|
||||
|
||||
基本用法是从一个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或者circ,coords支持三个值,分别表示中心点的x,y坐标和圆形半径r。
|
||||
- 矩形:rect或者rectangle,coords支持两个值,分别表示两个对角顶点x1,y1和x2,y2。
|
||||
- 多边形:poly或者polygon,coords至少包括6个值,表示多边形的各个顶点。
|
||||
|
||||
因为area设计的时间较早,所以不支持含有各种曲线的路径,但是它也是唯一一个支持了非矩形触发区域的元素,所以,对于一些效果而言,area是必不可少的。
|
||||
|
||||
area必须跟img和map标签配合使用。使用示例如下(例子来自html标准)。
|
||||
|
||||
```
|
||||
<p>
|
||||
Please select a shape:
|
||||
<img src="shapes.png" usemap="#shapes"
|
||||
alt="Four shapes are available: a red hollow box, a green circle, a blue triangle, and a yellow four-pointed star.">
|
||||
<map name="shapes">
|
||||
<area shape=rect coords="50,50,100,100"> <!-- the hole in the red box -->
|
||||
<area shape=rect coords="25,25,125,125" href="red.html" alt="Red box.">
|
||||
<area shape=circle coords="200,75,50" href="green.html" alt="Green circle.">
|
||||
<area shape=poly coords="325,25,262,125,388,125" href="blue.html" alt="Blue triangle.">
|
||||
<area shape=poly coords="450,25,435,60,400,75,435,90,450,125,465,90,500,75,465,60"
|
||||
href="yellow.html" alt="Yellow star.">
|
||||
</map>
|
||||
</p>
|
||||
|
||||
```
|
||||
|
||||
这个例子展示了在一张图片上画热区并且产生链接,分别使用了矩形、圆形和多边形三种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&utm_source=app&utm_medium=chongxueqianduan&utm_campaign=163-presell)
|
||||
Reference in New Issue
Block a user