GraphQL 概览

GraphQL

GraphQL 是一个用于 API 的查询语言,使用基于类型系统来执行查询的服务端运行时,并且它不和任何特定数据库或存储引擎绑定,依靠你现有的代码和数据支撑。

一个 GraphQL 服务通过定义类型和类型上的字段创建,并为每个类型上的字段提供解析函数。

为什么使用 GraphQL

传统的 REST 标准在使用中有一些问题:

  • 获取比较复杂的对象需要向服务器进行多次请求
  • REST 通常在服务端就决定了数据的结构,这导致你获得的数据很多是你不要的,即冗余数据颇多
  • 服务器返回的数据不可预测,你需要付出额外的工作来准确的理解你从服务器获得数据

而通过 GraphQL 你可以从服务器获取你期望的数据,并且清楚的知道你获得的数据是什么样子的。

查询和变更

字段 Fields

字段可以是标量或对象,当子段位对象类型时,你可以对其进行次级选择。GraphQL 查询能够遍历相关对象及其字段,是的客户端可以一次请求查询大量相关数据。

参数 Arguments

在 GraphQL 中,每一个字段和嵌套对象都能有自己的一组参数,从而使得 GraphQL 可以完美替代多次 API 请求。

参数可以是多种不同的类型。GraphQL 自带一套默认类型,但是你可以声明一套自己的定制类型,你要能序列化成的你需要的格式即可。

1
2
3
4
5
6
{
queryFunction(arg: arg) {
field,
...
}
}

别名 Aliases

为字段设置别名,可以使你能够通过不同参数来查询相同的字段。

片段 Fragments

片段是 GraphQL 中的可复用单元,它能够组织一组字段,在需要的地方被引入。

片段也可以访问查询或变更中声明的变量。

操作名称 Operation name

1
query/mutation/subscription operation_name {}

操作类型可以是 query/mutation/subscription,描述你打算做什么类型的操作。操作类型是必须的,除非使用查询简写语法 - 这种情况下,你无法为操作提供名称或变量定义。

操作名称是你的操作的有意义和明确的名称,它仅在有多个操作的文档中是必须的。但是操作名称在调试和服务端日志记录中非常有用,可以帮助快速定位到问题发生的位置。

变量 Variables

1
2
3
4
5
query query_function($variable_name: variable_type) {
obj(variable: $variable_name) {
...
}
}

所有声明的变量都必须是标量、枚举类型或输入对象类型。所以如果想要传递一个复杂对象到一个字段上,你必须知道服务器上其匹配的类型。

变量定义可以是可选的或者必要的。但是如果你传递变量的字段要求非空参数,那变量一定是必要的。

可以通过在查询中对类型定义后面附带默认值的方式,将默认值赋给变量。

1
2
3
4
5
query query_function($variable_name: variable_type = "default_value") {
obj(variable: $variable_name) {
...
}
}

指令 Directives

指令可以动态的改变我们的查询结构。指令可以附着在字段或者字段包含的字段上,然后以任何服务端期待的方式来改变查询的执行。

  • @include(if: Boolean) 仅在参数为 true 时,包含此字段
  • @skip(if: Boolean) 如果参数为 true ,跳过此字段

变更 Mutations

技术上而言,任何查询都能够被实现为导致数据写入的操作,然而,一个规范任何导致写入的操作都应该显式通过变更来发送也是必要的。

一个变更可能包含多个字段,查询是并行执行,变更是线性执行。

内联片段 Inline Fragments

如果你查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
query numberOFAnimals($sp:Species!) {
animals(species: $sp) {
number,
// 仅在 animals 返回为 Mammal 时执行
... on Mammal {
isRare
}
// 仅在 animals 返回为 Human 时执行
... on Human {
sex
}
}
}

具名片段也可以用于同样的情况,因为具名片段总是附带了一个类型。

原字段

1
2
// 获得那个位置的对象类型名称
__typename

Schema 和类型

类型系统

GraphQL 查询语言基本上就是关于选择对象上的字段。因此一个 GraphQL 查询的结构和结果非常相似,这使我们能预测服务器会返回什么样的结果。

每一个 GraphQL 服务都会定义一套类型,用来描述你能从服务器能够查询到的数据。每当查询到来,服务器就会根据 schema 验证并执行查询。

类型语言

GraphQL 可以用任何语言编写,因为它并不依赖于任何特定语言的句法来与 GrapahQL schema 沟通,它定义了称之为 GraphQL schema language 的简单语言。

对象类型和字段 Object Types and Fields

GraphQL schema 中最基本的组件是对象类型,它就表示你可以从服务上获取到什么类型的对象,以及这个对象有什么字段。

1
2
3
4
type Object_Name {
field,
...
}

参数 Arguments

GraphQL 对象类型上的每个字段都可能有零个或多个参数,并且所有参数都必须是具名的。参数可能是必选的或可选的,如果参数可选的,可以给它定义默认值。

查询和变更类型 The Query and Mutation Types

每一个 GraphQL 服务中都有一个 query 类型,可能有一个 mutation 类型,它们定义了每一个 GraphQL 查询的入口。

1
2
3
4
schema {
query: Query,
mutation: Mutation
}

标量类型 Scalar Types

一个对象类型有自己的名字和字段,而某些时候,这些字段必然会解析到具体数据,这就是标量,表示对应 GraphQL 查询的叶子节点。

它们包括 :

  • Int : 有符号 32 位证书
  • Float :有符号双精度浮点值
  • String : UTF-8 字符序列
  • Booleantruefalse
  • ID : ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键

你可以自定义标量类型,但需要保证其能够序列化、反序列化和验证。

枚举类型 Enumeration Types

枚举类型是一种特殊的标量,它限制在一个特殊的可选值集合内。

列表和非空 List and Non-Null

类型修饰符 ! 可将字段标注为非空,即表示这个字段总是会返回一个非空值。亦可用于修饰定义字段上的参数。

1
2
3
type Object_Name {
field_1: Type!
}

使用 [] 标记一个类型为 List ,表示这个字段会返回这个类型的数组。

1
2
3
query query_name {
field_list: [String],
}

接口 Interface

GraphQL 支持接口,接口是一个抽象类型,它包含某些字段,而想要实现这个接口的对象必须包含这些子段。

1
2
3
4
5
6
7
8
9
10
11
interface Human {
name: String!
age: Int!
}

type Man implements Human {
name: String!
age: Int!
height: Float!

}

联合类型 Union Type

联合类型和接口非常相似,但是不指定类型之间的任何共同字段。

1
union union_type = type_1 | type_2 | ...

联合类型的成员需要是具体对象类型。

输入类型 Input Types

使用输入类型可以将复杂的对象传递给字段,这在变更中特别有用。

1
2
3
4
input input_type {
field,
....
}

输入对象类型上的字段本身也可以只带输入对象类型,但是注意不能混淆输入和输出类型,输入对象类型上的字段不能拥有参数。

验证

通过使用类型系统,可以预判一个查询是否有效。这使得服务器和客户端可以在无效查询创建时就有效的通知开发者,而不用依赖运行时检查。

以下是指的注意的基本原则

  • 片段不能引用自身或创造回环
  • 只能查询给定类型上的字段
  • 如果查询返回的不是标量或枚举类型,就需要指明想要从字段中获取的数据

执行

一个 GraphQL 查询在被验证之后,GraphQL 服务器将会将其执行,并返回与请求的结构相对应的结果,该结果通常会是 JSON 格式。

每个类型的每个字段都由一个 resolver 函数支持,该函数由 GraphQL 服务器开发人员提供 - 当一个字段被执行时,相应的 resolver 函数被调用以产生下一个值。如果字段产生标量值,则执行完成。如果一个字段产生一个对象,则该查询将继续执行该对象对应字段的解析器,直到生成标量值。GraphQL 查询始终以标量值结束。

根字段 & 解析器

每一个 GraphQL 服务端应用的顶层,必有一个代表着所有进入 GraphQL API 可能的入口点,称之为 Root 类型或 Query 类型。

1
2
3
4
5
6
7
8
9
10
11
// `obj` 代表上一级对象,如果字段是根节点查询类型通常不会被使用
// `args` 提供在 GraphQL 查询中传入的参数
// `context` 会提供给所有解析器,持有重要的上下文信息
// `info` 保存与当前查询相关的字段的特定信息以及 `schema` 详细信息的值
Query {
query_name(obj, args, context, info) {
return context.db.query(...).then((return_data) => {
return return_data;
});
}
}

当每个字段被解析后,结果被放到键值映射中,字段名称作为键值映射的键,解析器的值作为键值映射的值,这个过程从查询字段的底部叶子节点开始返回,直到根 Query 类型的起始节点。最后合并成为能够镜像到原始查询结果的结果,让后将其发送到请求的客户端。

异步解析器

context 提供了一个数据库访问对象,用来通过查询中传递的参数查询数据,因为从数据库拉取数据是一个异步操作,它会返回一个 Promise 对象,当数据库查询返回结果,就能构造并返回查询结果。

只有解析器能够感知 Promise 的进度,GraphQL 查询只关注一个结果是否返回,在执行期间如果异步操作没有完成,则 GraphQL 会一直等待下去。

内省

GraphQL 通过内省系统使得我们能够直到 GraphQL Schema 支持哪些查询。

我们可以通过 __schema 字段来向 GraphQL 询问哪些类型是可用的。一个查询的根类型总是有 __schema 这个字段。

1
2
3
4
5
{
__schema {
...
}
}

检验特定类型

1
2
3
{
__type() {}
}

服务端渲染

服务器端渲染

什么是服务器端渲染?

将组件或页面在服务器端渲染为 HTML 字符串,直接发送到浏览器,最后将这些静态标记激活为客户端上完全可交互的应用程序。

为什么使用服务器端渲染?

SSR 的主要优势

  • 更好的 SEO

    搜索引擎爬虫工具可以直接查看完全渲染的页面

  • 更快的内容达到时间

    无需等待所有的 JavaScript 都完成下载并执行

需要权衡的地方

  • 浏览器特定的代码,只能在某些生命周期钩子函数中使用

  • 涉及构建设置和部署的更多要求

    服务器渲染应用程序,需要处于 Node.js server 环境

  • 更多的服务器端负载

防抖和节流

防抖和节流

处理频繁触发的事件时,如果没有进行限制,每次触发都调用处理函数进行处理不仅会浪费资源,还会降低用户体验,所以需要限制处理函数的调用频率,这便是用到防抖和节流的地方。

函数防抖

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

函数防抖的一个典型应用场景是根据 input 的输入内容搜索相关信息,如果 input 内容每次改变都发起搜索请求,则会在短时间内发次多个请求,这种情况下,对事件处理函数进行防抖处理,在给定的时间段 T 内,input 内容没有改变,才发起搜索请求。

1
2
3
4
5
6
7
8
9
10
// 一个简单的防抖函数
function debounce(fn, wait) {
let timeout = null;
return function() {
if (timeout !== null) {
clearTimeOut(timeout);
}
timeout = setTimeOut(fn, wait);
};
}

函数节流

当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

函数节流适合的处理的情况是更高频次的触发事件处理函数的情况,比如 resizemousemovescroll 等,强制事件处理函数以固定频率执行。

  • 时间戳实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 利用时间戳实现节流函数
function throttle(fn, delay) {
let prev = Date.now();
return function() {
let context = this;
let args = arguments;
let now = Data.now();
if (now - prev >= delay) {
fn.apply(context, args);
prev = Date.now();
}
};
}

// 事件处理函数
function handler() {}

// 事件监听
window.addEventListener('scroll', throttle(handler, 500))
  • 定时器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 利用定时器实现节流函数
function throttle(fn, delay) {
let timer = null;
return function() {
let context = this;
let args = arguments;
if (!timer) {
timer = setTimeOut(function() {
fn.apply(context, args);
timer = null;
}, delay)
}
};
}

// 事件处理函数
function handler() {}

// 事件监听
window.addEventListener('scroll', throttle(handler, 500))
  • 混合实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function throttleFun(fun, duration) {
let timer = null;
let startT = Date.now();
return function() {
const context = this;
const args = arguments;
const remaining = duration - ( startT - Date.now());
clearTimeout(timer);
if (remaining <= 0) {
fun.apply(context, args);
startT = Date.now();
} else {
timer = setTimeout(fun, remaining);
}
}
}

防抖和节流的区别

防抖将几次操作合并为一次操作,节流则保证一段时间内只触发一次函数。

函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。

CSS 备忘

媒体查询

1
2
// 基本形式
@media <media-type> and|not|only (<media-feature>) { css-code ... }

媒体类型

类型 描述
all 所有设备
screen 电脑屏幕,平板电脑,智能手机等(默认值,可省略)
print 打印机和打印预览
speech 屏幕阅读器等发声设备

媒体特征

https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries


元素定位

  • 相对定位 relative

    遵循相对定位的元素相对于在文档中的正常位置偏移给定的值,但不会影响其它元素的偏移。

    它原先占据的位置将会被保留。

  • 绝对定位 absolute

    相对定位的元素并未脱离文档流,而绝对定位的元素则脱离了文档流。在不知文档流中的其它元素时,绝对定位的元素不占据空间,它相对于最近的非 static 祖先元素定位。当这样的祖先元素不存在时, 则相对于 ICB(Initial Container Block) 定位。

  • 固定定位 fixed

    固定定位元素与绝对定位相似,但其包含块为 viewport

    但当固定定位的元素的祖先的 transform 属性为非 none 时,定位参考更改为该祖先元素。

    viewport 是一个网页的用户可视区域

  • 粘性定位 sticky

    粘性定位可以被认为是相对定位和固定定位的结合:元素在跨越特定阈值前为相对定位,之后为固定定位。

    必须指定 top, right, bottom, left 四个阈值其中之一,才能使粘性定位生效,否则其行为和相对定位相同。


CSS 选择器

简单选择器

  • 类型选择器

    element { style properties }

    选择所有匹配给定的节点名称的元素

  • 类选择器

    .className { style properties }

    选择所有拥有给定的类属性的元素

  • ID 选择器

    #idname

    基于 id 属性的值来选择一个元素,在一个文档中拥有指定的 ID 的元素应该只有一个

  • 通用选择器

    *

    选择所有元素

  • 属性选择器

    [attr] [attr=value] [attr~=value] [attr|=value] [attr^=value] [attr$=value] [attr*=value]

    根据给定的属性的值来选择元素

使用连接符

  • 兄弟选择

    A ~ B

    选择在 A 之后的所有 B 元素,且 A、B 拥有共同的父元素

  • 相邻兄弟选择

    A + B

    选择紧跟在 A 元素后的 B 元素,且 A 、B 拥有共同的父元素

  • 后代选择

    A B

    选择作为 A 元素的后代的 B 元素

  • 子元素选择

    A > B

    选择作为 A 元素直接后代的 B 元素

伪类

selector:pseudo-classes { style property }

用于向某些选择器添加特殊的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 表示被用户激活(触发)的元素,常用于 <a> 和 <button>
:active
// 表示任何 radio、checkbox 或 option 元素被选中
:checked
// 表示任何表单元素的默认状态
:default
// 表示任何被禁用的元素
:disabled
// 表示任何没有子元素的元素
:empty
// 表示元素处于可用状态,即可被激活、选中、按下、输入或获得焦点等
:enabled
// 用于 @page at-rule,表示被打印文档的第一页
:first
// 表示一组兄弟元素中的第一个
:first-child
// 表示一组兄弟元素中同类型的第一个元素
:first-of-type
// 表示用户使用指向设备和元素交互但是没有激活它的状态
:hover
// 表示任何处于 indeterminate 状态的表单元素
:indeterminate
// 表示 <input> 元素的当前值是否处于由 min 和 max 属性限制的的范围之内
:in-range
:out-range
// 表示 <input> 或其它表单元素的内容是否校验失败
:invalid
:valid
// 一组兄弟元素中的最后一个元素
:last-child
// 一组兄弟元素中同类型的最后一个元素
:last-of-type
// 表示一个元素还未被访问,匹配任何未被访问的拥有 href 属性的 <a> <area> 或 <link> 元素
:link
// 选择任何非指定元素的元素
:not()
// 选择一组兄弟元素中的指定位置的元素
:nth-child()
// 选择一组兄弟元素中的最末尾位置的元素
:nth-last-child
// 选择一组兄弟元素中的同类型的最后一个元素
:nth-last-of-type
// 在一组兄弟元素中选择同类型的元素中指定位置的元素
:nth-of-type()
// 选择一个没有任何兄弟元素的元素
:only-child
// 选择一个没有任何同类型兄弟元素的元素
:only-of-type
// 根据是否设置了 required 属性来选择 <input> <select> 或 <textarea> 元素
:optional
:required
//
:target

伪元素

element::pseudo-elements { style property }

用于向某些选择器添加特殊的效果

1
2
3
4
5
6
7
8
9
10
// 在指定的元素中的末尾创建一个子元素,经常使用 content 属性来向指定的元素添加装饰
::after
::before
//
::first-line
::first-letter
// 选中已经被用户高亮的部分
::selection
//
::slotted

CSS 基础概览

CSS 构造块

CSS (层叠样式表)是一种文本文件,其中包含一个或多个(通过属性和值)决定网页某特定元素如何显示的规则。CSS 里有控制基本格式的属性(如 font-size 和 color),有控制布局的属性(如 position 和 float),还有决定访问者打印时在哪里换页的打印控制 元素。CSS 还有很多控制元素显示或消失的动态属性,可以用于创建下拉列表和其他交互性组件。

CSS2 是目前浏览器支持最为广泛的版本,CSS3 是以CSS2 为基础,提供了大量设计人员和开发人员长期期待的功能,但可惜的是目前还没有成为规范。

构造样式规则

样式表中包含了定义网页外观的规则。

样式表中的每条规则都有两个主要部分 :选择器和声明块。选择器决定哪些元素受到影响;声明块由一个或多个 属性 - 值

对组成,它们指定应该做什么。

1
2
3
4
5
6
7
8
9
selector {
property-name:property-value;
···
}

h1 {
background-color: green;
color: red;
}

在样式规则中可以添加额外的空格、制表符或回车,从而提高样式表的可读性 。

为样式规则添加注释

在 CSS 中添加注释是个好主意,这样就可以标注样式表的主要区域,或者只是针对某条规则或声明进行解释。注释不仅对开发 人员有用,对阅读代码的其他人也有好处。

注释可以包含回车,因此可以跨越多行。类似地,/* 和 */ 既可以单独成行,也可以跟文本同处一行。另外,注释还可以放到声明块内部,或者样式规则后面。

1
2
3
4
5
6
7
8
9
10
11
/* This is a single line comment */
/*
···
This is multi-ling comment
···
*/
h1 {
color: green;
font-size: .875em;
/* This is a comment in declarations bloc */
}

需要注意的是,不能将一个注释嵌套在另一个注释内部 ,这样的做法是错误的。

注释是很有用的组织工具。样式表很快就会变得很长,因此,组织好样式表对于保持 CSS 易于维护至关重要。通常,将相关的规则放在一起,形成分组,并在每组前面放置一段描述性的注释

1
2
3
4
5
6
/* Section-1 */
···
/* Section-2 */
···
/* Section-3 */
···

当然,你也可以注释掉属性值,来进行调试,就像你在其它语言中做的那样。

CSS 中的继承

当你为某个元素应用 CSS 属性时,这些属性不仅会影响该元素,还会影响其下的分支元素。也就是说,这些下层的元素继承了其祖先元素的属性。

任何一个直接包含在另一个元素中的元素,都是其父元素的分支。

此外,对大多数属性来说,还可以使用 inherit 值进行强行继承。

那么,哪些属性会被继承呢?

  • 文本
    • color(颜色,a 元素除外) ‰
    • direction(方向)
    • font(字体)
    • font-family(字体系列) ‰
    • font-size(字体大小)
    • font-style(用于设置斜体)
    • font-variant(用于设置小型大写字母) ‰
    • font-weight(用于设置粗体)
    • letter-spacing(字母间距)
    • line-height(行高)
    • text-align(用于设置对齐方式)
    • text-indent(用于设置首行缩进)
    • text-transform(用于修改大小写)
    • visibility(可见性)
    • white-space(用于指定如何处理空格) ‰
    • word-spacing(字间距)
  • 列表
    • list-style(列表样式)
    • list-style-image(用于为列表指定定制的标记) ‰
    • list-style-position(用于确定列表标记的位置) ‰
    • list-style-type(用于设置列表的标记)
  • 表格
    • border-collapse(用于控制表格相邻单元格的边框是否合并为单一边框) ‰
    • border-spacing(用于指定表格边框之间的空隙大小)
    • caption-side(用于设置表格标题的位置)
    • empty-cells(用于设置是否显示表格中的空单元格)
  • 页面设置
    • orphans(用于设置当元素内部发生分页时在页面底部需要保留的最少行数) ‰
    • page-break-inside(用于设置元素内部的分页方式)
    • widows(用于设置当元素内部发生分页时在页面顶部需要保留的最少行数)
  • 其它
    • cursor(鼠标指针)
    • quotes(用于指定引号样式)

层叠:当规则发生冲突时

样式的来源很多,每个浏览器都有其默认的样式,你可以使用自己的样式覆盖它们或对它们进行补充。

有时候,多条规则会定义元素的同一个属性,这是应该怎么办呢?CSS 用层叠的原则来考虑样式声明,从而判断相互冲突的规则中哪个规则应该起作用。

首先,你编写的样式如果与浏览器的默认样式冲突,均以你编写的样式为准。在此基础上,CSS 用层叠的原则来考虑特殊性(specificity)、顺序(order)和重要性(importance),从而判断相互冲突的规则中哪个规则应该起作用。

  • 特殊性

    特殊性规则指定选择器的具体程度。选择器越特殊,规则就越强。遇到冲突时,优先应用特殊性强的规则 。

    一般来说,我们任务 id 选择器是最特殊的,而继承的样式是最一般的。

    不过,建议在样式表中多使用类选择器,避免使用 id 选择器。

  • 顺序

    有时候,特殊性还不足以判断在相互冲突的规则中应该优先应用哪一个。在这种情况下,规则的顺序就可以起到决定作用:晚出现的优先级高。

  • 重要性

    如果这还不够,可以声明一条特殊的规则覆盖整个系统中的规则,这条规则的重要程度要比其他所有规则高。也可以在某条声 明的末尾加上 !important,比如 p{ color: orange !important; }(除非是在特殊情况 下,否则不推荐使用这种方法)。

你编写的样式会覆盖浏览器的默认样式。 当两个或两个以上的样式发生冲突时,会应用特殊性高的样式声明,不管它位于样式表中的哪个位置。如果两个或两个以上的规则拥有相同的特殊性,则使用后出现的规则, 除非其中某条规则标记了 !important。

如果某元素没有直接指定某条规则,则使用继承的值(如果有的话)。

属性的值

每个 CSS 属性对于它可以接受哪些值都有不同的规定。有的属性只能接受预定义的值。有的属性接受数字、整数、相对值、百分数、 URL 或者颜色。有的属性可以接受多种类型的值。

  1. inherit

    对于任何属性,如果希望显式地指出该属性的值与对应元素的父元素对该属性设定的值相同,就可以使用 inherit 值。

  2. 预定义的值

    大多数 CSS 属性都有一些可供使用的预定义值。例如,float 属性可被设为 left、right 或 none。与 HTML 不同,不需要(也不能)将预定义的值放在引号里。实际上, 大多数 CSS 值,无论是否为预定义的值,都不需要加引号。(也有一些例外,如超过一个单 词的 font-family 名称,以及要生成的内容。)

  3. 长度和百分数

    很多 CSS 属性的值是长度。所有长度都必须包含数字和单位,并且它们之间没有空 格。例如 3em、10px。唯一的例 外是 0,它可以带单位也可以不带。效果是一样的,因此 0 一般不带单位。

    有的长度是相对于其他值的。一个 em 的长度大约与对应元素的字号相等。一般来说,应该只在输出尺寸已知的情况下使用绝对长度 。

    百分数(如 65%)的工作方式很像 em, 它们都是相对于其他值的值 ,百分数通常是相对于父元素的 。

  4. 纯数字

    只有极少数的 CSS 属性接受不带单位的数字,典型的如 line-height、z-index 和 opacity。

  5. URL

    有的 CSS 属性允许开发人员指定另一个文件的 URL,background-image 就是这样一个属性。

    1
    background: url(bg-patern.png);

    注意,规范指出,相对 URL 应该相对于样式表的位置而不是相对于 HTML 文档的位置。

  6. CSS 颜色

    我们可以使用预定义颜色关键字或以十六进制(通常称为 hex)、RGB、HSL、 RGBA、HSLA 等格式表示的值中为 CSS 属性指定颜色。

    实践中,定义 CSS 颜色更常规的方法是使用十六进制格式或 RGB 格式。如果要指定透明度的话,请使用 RGBA。

  7. RGB

    可以通过指定红、绿、蓝(这也是 RGB 这一名称的由来)的量来构建自己的颜色。 可以使用百分数、0 ~ 255 之间的数字来指定这三种颜色的值。

    1
    color: rgb(0,0,0);
  8. 十六进制数

    1
    color: #FFFFF;
  9. RGBA

    RGBA 在 RGB 的基础上加了一个代表 alpha 透明度(alpha transparency) 的 A。 alpha 透明度的一种常见使用场景是将其用在对元素设置 background-color 或 background 的情况(均用于设置背景),因为 alpha 透明度允许元素下面的任何东西(如图像、其他颜色、文本等)透过来并与元素混合在一起。 也可以对其他基于颜色的属性使用 alpha 透明度,如 color、border、border-color、box- shadow、text-shadow 等。

    1
    2
    3
    4
    5
    6
    /* 不透明,和 rgb(89, 0, 127); 效果相同 */ background-color: rgba(89,0,127,1);
    /* 完全透明 */
    background-color: rgba(89,0,127,0);
    /* 25%透明 */
    background-color: rgba(89,0,127,0.75); /* 60%透明 */
    background-color: rgba(89,0,127,0.4);

操作样式表

将 CSS 应用到多个网页(包括整个网站)、单个页面或单独的 HTML 元素 ,有三种方式:外部样式表、嵌入样式表和内联样式表。外部样式表非常适合给网站上的大多数页面或者所有页面设置一致的外观。可以在一个外部样式表中定义全部样式,然后让网 站上的每个页面加载这个外部样式表,从而确保每个页面都有相同的设置 。

外部样式表是作为操作样式表的最佳实践。

链接到外部样式表

将已经编写好的样式表加载到 HTML 页面中去,最好的方式是链接到样式表。对外部样式表进行修改时,所有引用它的页面也会自动更新 。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CSS</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
</body>
</html>

link 元素位于 HTML 文档的 head 部分。 页面可以包含一个以上的 link 元素,但使用它的次数最好尽可能地少,让页面得以更快地加载 。

外部样式表的另一个好处是,一旦浏览器在某个页面加载了它,在随后浏览引用它的页面时,通常无需再向 Web 服务器请求 该文件。浏览器会将它保存到缓存里,也就是保存到用户的计算机里,并使用这个版本的文件。这样做可以加快对页面的加载。如果随后对样式表作了修改,再将它传到 Web 服务器,浏览器就会下载更新后的文件,而不是使用缓存的文件(从技术上讲也有例外,但通常不会遇到这种情况)。

可以在页面中使用多个 link 元素, 从而加载多个样式表文件。如果在不同的文件中有显示声明产生冲突,则后面文件中的规则的优先级更高。

嵌入式样式表

嵌入样式表是在页面中应用 CSS 的第二种方式。我们在 HTML 页面的 head 部分创建一个 style 元素,其中包含了我们的样式表。由于这些样式只在一个网页里存在,因此不会像外部样式表中的规则那样应用到其他的页面,同时,缓存的好处也不存在了。如上文所述,对于大多数情况, 我们推荐使用外部样式表,但理解其他的选择以备不时之需也是很重要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CSS</title>
<style>
img {
border: 4px solid red;
}
</style>
</head>
<body>
</body>
</html>

使用嵌入样式表时,style 元素及其包围的样式规则通常位于文档的 head 部分。

当且仅当 style 元素出现在 link 元素后面的时候,嵌入样式表中的样式才会覆盖外部样式表中的样式。

应用内联样式

内联样式将内容和变现会在一起,严重违背了最佳规范。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CSS</title>
</head>
<body>
<img src="imgs/market.png" width="20" height="20" alt="" style="border: 4px solid red" />1
</body>
</html>

更好的做法是通过类名来应用外部样式表中的规则。如果你希望其它元素也是用这种样式,只需为其添加 class=”class-name” 即可。

内联样式的优先级高于其它所有样式,除非其它地方与之冲突的样式标记了 !important。

样式的层叠和顺序

将多个样式规则应用于同一元素的情况并不少见,大型网站更是如此,他们需要更多精力维护 CSS。有时候这些规则针对的是 同一条属性。在规则的特殊性相同的情况下,样式的顺序就成为关键。基本规则是:在其他条件相同的情况下, 越晚出现的样式优先级越高 。

以下解释了顺序如何决定样式规则的优先级:

  • 嵌入样式表(位于 style 元素内)与任何链接的外部样式表之间的关系取决于它们在 HTML 中的相对位置。
  • 内联样式(实际上直接应用于元素) 在外部样式表和嵌入样式表之后。由于顺序最靠后,其优先级是最高的。 一旦应用到任何地方,都会覆盖与之冲突的其他样式。
  • 关于相互冲突的样式的顺序对优先级的影响,有一种例外情况,就是标记 !important 的样式总是具有最高的优先级,无论它出现在最前、最后,还是中间。不过,要尽量避免这种用法,极端情况除外。几乎在全部情况下,使用选择器都能达到同样的效果。此外,!important 让声明变得太强,如果要覆盖这样的样式,就不得不借助于更长的规则。

有一种使用 !important 的合理情形。 有时网页会包含一些你无法修改的 HTML, 例如来自第三方服务的新闻源。如果这些 HTML 中有一些内联样式与你的设计不符, 你就可以在自己的样式表中使用 !important 覆盖这些样式。

使用与媒体相关的样式表

可以指定一个只用于特定输出的样式表, 如只用于打印,或只用于在浏览器中查看屏幕。

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html> 
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CSS</title>
<link rel="stylesheet" href="css/style.css" media="screen" />
<link rel="stylesheet" href="css/print.css" media="print" />
</head>
<body>
</body>
</html>

通过对 link 元素添加 media 属性,可 以将样式表限于特定输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 针对所有样式 */
img {
border: 4px solid red;
}
p {
color: orange;
font-style: italic;
}

/* 打印样式表 */
@media print {
body {
/* 加大字号 */
font-size: 25pt;
}
p{
/* 黑色的十六进制值 */
color: #000;
}
img {
/* 不显示图像 */
display: none;
}
}

联合使用 CSS3 引入的媒体查询和这里介绍的媒体输出类型可以形成强大的威力。 通过这些技术,可以根据输出设备的属性确 定需要应用到页面的样式。

定义选择器

CSS 样式规则有两个主要部分。选择器决定将格式化应用到哪些元素,而声明则定义要应用的格式化。

构造选择器

选择器可以定义五个不同的标准来选择要进行格式化的元素。

  • 元素的类型和名称

    1
    2
    3
    h1 {
    color: red;
    }
  • 元素所在的上下文

    1
    2
    3
    4
    /* em 为元素名称,这个选择器只应用于 h1 元素中的 em 元素*/
    h1 em {
    color: red;
    }
  • 元素的类或 id

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* 选择所有属于 error 类的元素 */
    .error {
    color: red;
    }

    /* 选择 id 为 gaudi 的元素 */
    #gaudi {
    color: red;
    }

    通过在 class 或 id 选择器前面添加目标元素的名称,可以增强选择器的特殊性。

    1
    2
    3
    4
    /* 选择属于 error 的 strong 元素 */
    strong.error {
    color: red;
    }

    如果要定位的元素有多个类名,可以在选择器中将它们连在一起。

  • 元素的伪类或伪元素

    1
    2
    3
    4
    /* 选择属于 link 伪类的 a 元素 */
    a:link {
    color: red;
    }
  • 元素是否有某些属性的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* 选择所有 title 属性的 a 元素 */
    a[title] {
    color: red;
    }

    /* 选择指向维基百科的 a 元素 */
    a[href="https://www.wikipedia.ort"] {
    color: red;
    }

编写 CSS 的一个重要目标就是让选择器尽可能的简单,仅保持必要的特殊性。应该充分利用样式的层叠特性,即元素的后代会继承祖先元素的样式。同时,应该将页面中的通用设计元素挑选出来,编写一个选择器(如类名),从而可以在全站不同的元素上共享该样式。

按照名称选择元素

选择要格式化的元素,最常用的就是按照元素的名称进行选择。比如 :

1
2
3
h1 {
color: red;
}

除非指定其它情况,指定类型的所有元素都将被格式化,无论它们出现在文档的什么位置。

通配符 * 匹配代码中的任何元素名称。但是因为匹配范围太广,一般不建议使用,因为这会使得浏览器加载页面变慢。

可以在一个选择器中使用一组元素名称,名称之间用逗号分隔。

1
2
3
h1,h2,h3 {
color: red;
}

按类或 ID 选择元素

如果已经在元素中标识了 class 或 id ,就可以在选择器中使用这个标准,从而只对已标识的元素进行格式化。

可以单独用使用 class 和 id,也可以同其它选择器标准混在一起使用,比如

1
2
3
4
5
6
7
8
// 会影响所有属于 news 类的元素
.news {
color: red;
}
// 只会影响属于 news 类的 h1 元素
h1.news {
color: red;
}

除非必须特别针对目标元素,否则不要在 id 或 class 选择器中添加元素名称。

如果要定位的类名有多个,可以在选择器中将它们连在一起

1
2
3
.class-news.class-video {
color: blue;
}

这里需要主要的是,任何 .class-news.class-video 选择器的规则仍会应用于该元素,但是 .class-news.class-video 的规则的特殊性更高。

按上下文选择元素

在 CSS 中,可以根据元素的祖先、父元素或同胞元素来定位它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 组合使用了类选择器和类型选择器
// 这将作用于任何作为 class-news 类元素的后代的 p 元素
.class-news p {
color: red;
}
// 作用于所有以 article 元素为祖先的 p 元素
article p {
color: red;
}
// 作用于以属于 class-news 类的 article 元素为祖先的任何 p 元素
// 这种类型的选择器的特殊性最高,通常会比实际需要的特殊性更高
article.class-news p {
color: red;
}

祖先元素是包含目标元素的任何元素,不管它们之间相隔多少代,而父元素是直接包含另一个元素的元素,它们之间只隔一代。

通常将基于祖先元素的选择器成为后代选择器,不过 CSS 3 中将其称为后代结合符。

按照祖先元素选择要格式化的元素

1
2
3
element-ancestor element-target {
···
}

按照父元素选择要格式化的元素

1
2
3
element-father > element-target {
···
}

按照相邻同胞元素选择要格式化的元素

CSS 相邻同胞结合符可以选择直接跟在指定的同胞元素后面的同胞元素。

1
2
3
.class-news p+p {
···
}

选择第一个或最后一个子元素

有时候需要选择仅作为某元素第一个或最后一个子元素的元素,这时候就要用到 :first-child:last-child 伪类。

1
2
3
4
5
6
7
8
9
10
11
12
// 选择子元素中的第一个 li 元素
li:first-child {
color: red;
}
// 选择子元素中的最后一个 li 元素
li:last-child {
color: red;
}
// 选择任何作为另一个元素的第一个子元素
:first-child {
color: red;
}

在伪类之前添加其它选择器可以使其特殊性更高

1
2
3
4
// 仅对 class-news 类的第一个 h1 子元素起作用
.class-news h1:first-child {
color: red;
}

选择元素的第一个字母或第一行

使用 :first-letter:first-line 伪元素只选择元素的第一个字母或第一行。

1
2
3
4
5
6
7
8
9
10
// 选择 p 元素的第一个字母
p:first-letter {
color: red;
font-size: 1.4em;
font-weight: bold;
}
// 选择 p 元素的第一行
p:first-line {
color: red;
}

可以将 :first-letter:first-line 伪元素与其它选择器结合使用

1
2
3
4
// 作用于 class-news 类中每个段落的第一个字母
.class-news p:first-letter {
color: red;
}

需要注意的是,只有某些特定的 CSS 属性可以应用于 :first-letter 伪元素,包括 font、color、background、text-decoration、vertical-algin、text-transform、line-height、margin、padding、border、float 和 clear。

伪元素、伪类及 CSS 3 语法

在 CSS 3 中

1
2
:first-line -> ::first-line
:first-letter -> ::first-letter

这样是为了将伪类和伪元素进行区分。

按照状态选择链接元素

CSS 允许根据链接的当前状态对其进行格式化。

链接的状态包括访问者是否将鼠标停留在链接上,链接是否被访问过等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 从未被激活或指向,当前也没有被激活或指向的链接
a:link {
color: red;
}

// 已激活过的链接
a:visited {
color: orange;
}

// 通过键盘选择并已准备好激活的
a:focus {
color: purple;
}

// 光标指向连接时
a:hover {
color: green;
}

// 激活的链接
a:active {
color: blue;
}

也可以对其它类型的元素使用 :active:hover 伪类

1
2
3
4
// 鼠标停留在任何段落上都会使其显示红色
p:hover {
color: red;
}

按照属性选择元素

可以对具有给定属性或属性值的元素进行格式化。

CSS 提供了多种方式匹配属性和属性值,包括只检查属性名、检查全部或者部分属性值。如果在选择器中呼略了属性值,就可以为具有给定属性的元素应用样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
element[attribute]			// 匹配指定属性,无论属性值是什么
[attribute="value"] // 完全匹配指定属性值
[attribute~="value"] // 匹配属性值以空格分隔的多个单词,其中有一个完全匹配指定值
[attribute|="value"] // 匹配以 value- 打头的属性值
[attribute^="value"] // 匹配以 value 开头的属性值,value 为完整的单词或完整的单词的一部分
[attribute$="value"] // 匹配属性值以 value 结尾,value 为完整的单词或完整的单词的一部分
[attribute*="value"] // 属性值为指定值的子字符串


// 选择所有具有 class 属性的段落
p[class] {
color: red;
}

指定元素组

我们经常需要将相同的样式规则应用于多个元素。可以为每个元素重复地设置样式规则,也可以组合选择器,一次性地设置样式规则。

1
2
3
4
5
6
element-1,
element-2,
···
element-n {
···
}

可以列出任意数量的单独的选择器,无论它们包含的是元素名称、类还是伪元素,只需用逗号将它们隔开。

通过元素组添加样式只是一种简写方式。

有时,为应用于多个选择器的共同样式创建一个样式规则,再为没有共同点的样式分别创建单独的样式规则是很有用的。要记住,在样式表中后指定的规则会覆盖先指定的规则。

为文本添加样式

使用 CSS 可以修改文本的字体、大小、粗细、倾斜、行高、前景和背景颜色、间距和对齐方式、添加下划线和删除线等。

选择字体系列

并非所有的系统都支持相同的默认字体,因此应该定义替代字体作为备选。

设置字体的方法

1
2
3
4
5
6
7
body {
font-family: Geneva;
}

h1h2 {
font-family: "Gill Sans";
}

由于 font-family 是继承属性,当我们对 body 元素设置了字体以后,该样式也会应用到其它元素。但是有几个元素不会继承父元素的字体设置,其中有表单的 select、textarea 和 input 元素。不过,可以强制它们继承父元素的字体。

1
2
3
input, select, textarea {
font-family: inherit;
}

指定替代字体

为了使得文本以你想要呈现的字体来展示,你需要设置替代字体以防止在某些系统上没有安装你指定的字体的情况。

1
2
3
4
5
6
7
8
9
// 浏览器会使用列表中的第一个已安装在访问者系统内的字体
body {
font-family: Geneva, Tahoma, sans-serif;
}

h1,
h2 {
font-family: "Gill Sans","Gill Sans MT";
}

OS X 和 Windows 上默认的共有的字体 它们仅包括 Arial、Comic、Sans MS、Courier New、Georgia、Impact、Trebuchet MS、Times New Roman 和 Verdana。

系统通常都有下列表示类属的字体名称对应的字体:serif、sans-serif、cursive、fantasy 和 monospace。因此,标准的做法就是字体栈的末尾指定上述字体名称中的一种。

可以在同一个 font-family 规则中为不同的字母表指定字体,从而对包含不同怨言和书写体系的文本进行格式化。

创建斜体

浏览器通常让一些 HTML 元素默认以斜体显示,因此,不比在 CSS 中对这些元素设置斜体。有时,你需要让一些内容以斜体显示,但是用元素却没有合适的语义,这时,使用 CSS 的 font-style 属性就可以让任何元素中的文本以斜体显示。

1
2
3
p {
font-style: italic;
}

因为 font-style 属性是继承的,如果你想要在某些地方不以斜体显示,则可以取消斜体

1
font-style: normal;

创建粗体

粗体格式可能是让文本突出显示的最常见、最有效的方式。

1
2
3
4
em,
a:link {
font-weight: bold;
}

设置 font-weight 属性的值为 bold,让文本显示为有平均加粗值的字体,或者输入 100 ~ 900 之间的 100 的倍数,其中 400 代表正常粗细,700 代表粗体。

设置 font-weight 为 normal 来取消粗体。

设置字体大小

为网页里的文本设置字体大小有两种方式:直接使用像素指定要使用的特定字号或使用百分数、em、rem 指定相对于父元素文本的大小。

1
2
3
4
5
6
7
h1 {
font-size: 35px;
}

h2 {
font-size: 28px;
}

使用 em 这样的相对单位有更大的灵活性,而且对定义页面中特定的设计部件的尺寸很有帮助。

1
2
3
4
5
6
7
8
9
10
11
12
// body 中设置的 font-size100% 声明为 em 字体大小设置了参考的基准。这里的 100% 将被翻译为默认字体大小
body {
font-size: 100%; /* 16px */
}

h1 {
font-size: 2.1875em; /* 35px/16px */
}

h2 {
font-size: 1.75em; /* 28px/16px */
}

使用 rem 设置字体大小

CSS 3 引入了一些新的单位,其中很有意思的一个便是 rem。它同 em 很像,不过它总是以根元素为参照系设置其它元素的字体大小,而不是父元素。这样做字体大小的设置。

设置行高

行高指的是段落的行距,即段落内诶航之间的距离。

1
2
3
class-news {
line-height: 1.5;
}

如果使用数字设定行高,那么所有的子元素都会继承这个因子。因此,如果父元素的字体大小是 16 像素或以 em 表示的等价大小,行高是 1.5 的话,则该元素的行高就是 24 像素。

同时设置所有字体值

可以使用 font 简写属性同时设置字体样式,但是需要注意的是 font 简写属性至少应该包括字体系列和字体大小属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
body {
font: 100% Geneva, Tahoma, Verdana, sans-serif;
}

h1,
h2 {
/* 这些声明无法使用 font 简记法,除非同时声明字体大小 */
font-family: "Gill Sans", "Gill Sans MT", Calibri, sans-serif;
font-weight: bold;
}

.class-news p {
/* 这些声明无法使用 font 简记法,除非同时声明字体大小 */
font-size: .9375em;
ling-height: 1.65;
}

设置颜色

1
2
3
4
5
6
7
body {
color: blue;
}

h2 {
color: #000;
}

除了 link 元素,其它元素都会继承 body 元素中的 color 属性,你必须显示设置 link 元素的 color 属性。

设置背景

设置背景有很多属性可以利用,包括 background-color、background-image、background-repeat、background-attachment 及 background-position 等。

1
2
3
4
5
6
7
8
9
10
11
12
body {
background-color: #88b2d2;
}

h2 {
background-color: #eaebef;
}
// 使用背景图像作为背景
h3 {
background-image: url(bg-pattern.png);
background-repeat: repeat-x;
}

background-color 的默认值是 transparent,background-image 的默认值是 none,background-repeat 的默认值是 repeat,background-attachment 的默认值是 scroll,background-position 的默认值时 0 0。只有在需要覆盖其它样式规则的时候才会显示的指定默认值。

控制间距

可以增加或减少单词之间或字母之间的距离,前者成为字间距,后者称为字偶距。

1
2
3
4
5
// word-spacingletter-spacing 是继承的
body {
letter-spacing: 1px;
word-spacing: 2px;
}

单词间距和字母间距还可能受到所选对齐方式的和字体系列的影响。

要将字母间距和单词间距设置为默认值,可使用 normal 或 0 。

添加缩进

通过设置 text-indent 属性,可以指定段落第一行前面应该空出多大的空间。

1
2
3
p {
text-indent: 2em;
}

其实我们也可以为其它元素应用 text-indent 属性,而不仅是段落元素,不过默认情况下对 em、strong、cite 等内联元素没有效果。可以将它们设置为 display:block;或 display: inline-block; 为其添加 text-indent 属性。

对齐文本

根据需要,可以让文本左对齐、右对齐、居中对齐或两端对齐。

1
2
3
p {
text-align: left; // right/center/
}

修改文本的大小写

使用 text-transform 属性,可以为样式定义文本的大小写。通过这种方法,可以将文本显示为首字母大写、全部大写、全部小写或原样显示。

1
2
3
4
5
6
{
text-transform: uppercase; // 让所有字母大写
text-transform:capitalize; // 让每个单词的首字母大写
text-transform:lowercase; // 让所有字母小写
text-transformnone; // 让文本保持原来的样子或用来取消继承的值
}

使用小型大写字母

很多字体都有对应的小型大写字母变体, 其中一些字母是大写的,但缩小到了小写字母的大小。可以使用 font-variant 调用小型大写字母变体 。

1
2
3
h2 {
font-variant: small-caps;
}

装饰文本

可以使用 text-decoration 属性装饰文本,如添加下划线或者其他类型的线条。目前最常用到这个属性的情况是为链接状态添加 样式 。

1
2
3
4
5
6
7
a:link {
color: #e1000;
text-decoration: none; // 无装饰样式
text-decoration: underline; // 下划线
text-decoration: overline; // 上划线
text-decoration: line-through; // 删除线
}

设置空白属性

默认情况下,HTML 文档里的多个空格和回车会显示为一个空格,或者被忽略。 如果要让浏览器显示这些额外的空格,可以使 用 white-space 属性 :

1
2
3
4
5
body {
white-space: nowarp; // 确保所有空格不断行,也就是文本全部显示在一行
white-space: pre; // 显示原文本中的所有空格和回车
white-space: normal; // 按正常方式处理空格
}

用CSS进行布局

使用 CSS 进行布局的注意事项

  • 内容与显示分离

  • 布局方法

    • 固定布局,真个页面和每一栏都有基于像素的宽度
    • 响应式页面也称为流式页面,使用百分数定义宽度,允许页面随显示环境的改变进行放大或缩小。
  • 浏览器注意事项

    并非所有的访问者都是用同样的浏览器,同样的操作系统,甚至同样的设备访问你的网站,因此通常在将网站放到服务器上发布之前,尽量在不同的操作系统用不同的浏览器进行充分的测试。

对默认的样式进行重置或标准化

每个浏览器都有内置的默认样式表,HTML 会遵照该样式显示,除非你自己编写的 CSS 覆盖了它们。整体上,不同浏览器提供的默认样式是相似的,但也存在一定的差异,为此,在应用自己的 CSS 之前,应抹平这些差异。

抹平差异的方法主要有两种 :

  • 使用 CSS 重置开始主样式表
  • 使用 Nicolas Gallagher 和 Jonathan Neal 创建的 normalizes.css 开始主样式表

CSS 重置可以有效地将所有默认样式都设为 ‘零’,而第二种方法,即 normalize.css,则采用了不同的方式,它并非对所有样式进行充值,而是对默认样式进行微调,是它们在不同的浏览器中具有相似的外观。

盒模型

CSS 处理网页时,它认为每个元素都包含在一个不可见的盒子里。这就是众所周知的盒模型,这里的盒子由内容区域、内容区 域周围的空间(内边距,padding)、内边距的外边缘(边框,border)和边框外面将元素与相邻元素隔开的不可见区域(外边距,margin)构成 。

宽度、高度和盒模型

关于 CSS 的 Width 属性对元素所显示宽度的影响,默认的处理方式实际上有悖常理。浏览器中元素的宽度与其 width 属性并不一致。CSS 中的宽度指示的是内边距里内容区域的宽度,而元素在浏览器中的显示宽度则是内容宽度、左右内边距和左右边框的总和。显示高度与之类似,只不过计算的是上下内边距和边框值。

通过对元素设置 box-sizing: border-box ,可以使元素的显示宽度就等于 width 属性的值。

控制元素的显示类型和可见性

每个元素在默认情况下要门显示在单独的行要么显示在行内,造成这种原因的本质是它们的 display 属性,即块级元素被设置为 display:block(对于 li 元素为 display:list-item,而行内元素被设置为 display:inline)。

不过,可以使用 CSS 改变元素的默认显示类型。

指定元素的显示方式 :

1
2
3
4
5
6
{
display: block; // 让元素显示为块级元素,就像开始新的段落
display: inline; // 让元素显示为行内元素
display: inline-block; // 让元素显示为行内元素,同时具有块级元素的特征
display: none; // 隐藏元素
}

控制元素的可见性 :

使用 visibility 属性可以控制元素是否可见。与 display 不同的是,使用 visibility 隐藏元素时,元素及其内容应该出现的位置会留下一片空白区域。

1
2
3
em {
visibility: hidden;
}

设置为 display:none;或 visibility:hidden;的元素里的所有内容,包括后代都会受到影响。

设置元素的高度和宽度

可以为很多元素设置高度和宽度,如分块内容、段落、列表项、div、图像、video、 表单元素等。

同时,可以为短语内容元素(默认以行内方式显示)设置 display: block; 或 display: inline-block;,再对它们设置宽度 或高度。

1
2
3
4
em {
width: w; // w 是元素内容区域的宽度,可以表示为长度或父元素的百分数
height: h; // h 是元素内容区域的高度,只能表示为长度
}

需要注意的是,内边距、边框和外边距都不包含在宽度或高度的数值里。

可以使用 * 通配符对所有元素应用 border-box 规则

1
2
3
4
5
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}

百分数是相对于父元素的宽度的,而不是相对于元素本身的原始宽度的。

如果不显式设置宽度和高度,浏览器就会使用 auto。对于大多数默认显示为块级元素的元素,width 的 auto 值是由包含块的宽度减去元素的内边距、边框和外边距计算出来的。简单说来, 包含块的宽度指的是父元素给元素留出的宽度。

对于图像这样的元素,auto 宽度等于它们固有的宽度,即外部文件实际尺寸大小。

除非你确定元素的内容不会变的更高,最好避免在样式表中指定高度。大多数情况下,可以让内容和浏览器自动控制高度。

在元素周围添加内边距

内边距就是元素内容周围、 边框以内的空间。 可以改变内边距的厚度,不能改变它的颜色或纹理。

1
2
3
4
.about {
background-color: #2b2b2b;
padding: .3125em .625em .625em;
}

同 border 和 margin 属性一样,padding 也可以使用简计法:

1
2
3
padding: 5px;		// 应用于全部四个边距
padding: 5px 10px; // 前一个应用于上下两边,后一个应用于左右两边
padding: 5px 10px 15px; // 分别应用于 上边 左右两边 下边

默认的情况下,元素的 width 和 height 不包含 padding 的大小,并且,内边距是不继承的。

当 em 值用于内边距和外边距时,它的值是相对于元素的字体大小的,而不是相对于父元素的字体大小的。

设置边框

可以在元素周围创建边框,或针对元素的某一边设置边框,并设置它的厚度、风格和颜色。边框可以应用于任何元素,包括图像。 如果希望对一个以上的元素设置相同的边框样式, 最好引入一个类,从而可以复用 。

1
2
3
.em {
border-top: 5px solid #019443;
}

定义边框的风格 :

1
2
3
4
5
6
7
8
9
border-style: none;
border-style: dotted;
border-style: dashed;
border-style: solid;
border-style: double;
border-style: groove;
border-style: ridge;
border-style: inset;
border-style: outset;

定义边框宽度和颜色 :

1
2
border-width: n;
border-color: color;

border 简写属性及各个边框属性 (border-width、border-style 和 border- color)均可接受一至四个值。如果使用一个值,那么它会应用于全部四个边。如果使用两个值,那么前一个值会应用于上下两边, 后一个值会应用于左右两边。如果使用三个值,那么第一个值会应用于上边,第二个值会应用于左右两边,第三个值会应用于下边。 如果使用四个值,那么它们会按照时钟顺序, 依次应用于上、右、下、左四个边。

设置元素周围的外边距

外边距是元素与相邻元素之间的透明空间。

1
2
3
em {
margin: 1.25em 0 .8em;
}

如果元素位于另一个元素的上面,对 于相互接触的两个 margin(即元素相互接触的下外边距和上外边距),仅使用其中较大的一个,另一个外边距会被叠加。左右外边 距不叠加。

使元素浮动

可以通过 float 属性使得元素浮动在文本或其它元素上。可以使用这种技术让文本环绕在图像或者其它元素周围,也可以使用这种技术创建多栏布局。

1
2
3
4
5
{
float: left; // 让元素浮动在左边,其它内容环绕在它右边
float: right; // 让元素浮动在右边,其它内容环绕在它左边
float: none; // 让元素不浮动
}

需要注意的是,float 属性是不继承的。

控制元素浮动的位置

1
2
3
4
5
6
{
clear: left;
clear: right;
clear: both;
clear: none;
}

使用 clear 属性清除浮动效果。如果对某个元素使用该属性,该元素和它后面的元素就会显示在浮动元素的下面。应该将 clear 属性添加到不希望环绕浮动对象的元素上。

浮动元素的 display 属性会变成 display: block; ,哪怕将其设置为 display: inline;,该属性值依然为 block。

浮动元素并不影响父元素的高度。如果要让浮动元素的祖先元素在高度上包含浮动元素,并消除浮动,可以使用 clearfix 或 overflow 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
.clearfix::before,
.clearfix::after {
content: " ";
display: table;
}

.clearfix::after {
clear: both;
}

.clearfix {
*zoom: 1;
}

对元素进行相对定位

每个元素在页面的文档流中都有一个自然位置。相对于这个原始位置对元素进行移动就称为相对定位。

1
2
3
4
5
6
7
{
position: relative;
top: 16px;
right: 1em;
bottom: 16px;
left: 1em;
}

其它元素不会受到便宜的影响,仍然按照这个元素原来的盒子进行排列。设置了相对定位的内容可能与其它内容重叠。

使用相对定位、绝对定位或固定定位时,对于相互重叠的元素,可以用 z-index 属性指定它们的叠放次序。

对元素设置 position: static,可以覆盖 position: relative 设置。static 是元素的默认值,这就是元素出现在常规文档流中的原因。

而且定位是不继承的。

对元素进行绝对定位

正如前面提到的,网页中的元素通常按照它们在 HTML 源代码中出现的次序进行排列,除非你使用 CSS 规则改变前面的样式。而通过对元素进行绝对定位,即指定它们相对于 body 或最近的已定位祖先元素的精确位置,可以让元素脱离正常的文档流。这与相对定位不同,绝对定位的元素不会在原先的位置留下空白。这与让元素浮动也不同。对于绝对定位的元素,其他元素不 会环绕在它的周围。事实上,其他内容不知道它的存在,它也不知道其他内容的存在。

1
2
3
4
5
{
position: absolute;
top: 32px;
right: 2em;
}

由于绝对定位的元素脱离了文档流, 因此它们可能会相互重叠,或与其他元素重叠 。如果不为绝对定位的元素指定偏移量,这个元素将显示在它的自然位置上,但不会影响后续元素在文档流中的位置。

还有一种定位类型称为固定定位。 当访问者滚动浏览器窗口时,页面内容通常随之上下移动。如果对元素设置 position: fixed;,它就会固定在浏览器窗口中。当访问者上下滚动浏览器窗口时,该元素不会随之移动,页面的其余部分仍照常滚动。

在栈中定位元素

当你开始使用相对定位、绝对定位和固定定位以后,很可能发现元素相互重叠的情况,这时可以选择哪些元素应该出现在顶层。

1
2
3
{
z-index: 100; // z-index 的值越大,元素在堆中就越高。
}

注意,z-index 属性仅对定位过的元素有效且是不继承的。

处理溢出

当元素溢出盒子时,可以使用 overflow 属性控制元素在盒子外面的部分。

1
2
3
4
5
6
{
overflow: visible; // 让元素盒子中的所有内容可见,默认项
overflow: hidden; // 隐藏盒子容纳不了的内容
overflow: scroll; // 无论元素是否需要,都在元素上添加滚动条
overflow: auto; // 让滚动条仅在访问者访问溢出内容时出现
}

垂直对齐元素

可以使用除默认对齐方式以外的多种方式对齐元素,让它们在页面上显得较为整齐 。

1
2
3
4
5
6
7
8
9
10
{
vertical-algin: baseline; // 使元素的基准线对齐父元素的基准线
vertical-algin: middle; // 使元素位于父元素的中央
vertical-algin: sub; // 使元素成为父元素的下标
vertical-algin: super; // 使元素成为父元素的上标
vertical-algin: text-top; // 使元素的顶部对齐父元素的顶部
vertical-algin: text-bottom;// 使元素的底部对齐父元素的底部
vertical-algin: top; // 使元素的顶部对齐当前行里最高元素的顶部
vertical-algin: bottom; // 使元素的顶部对齐当前行里最低元素的底部
}

修改鼠标指针

一般情况下,由浏览器负责控制鼠标指针的形状。

1
2
3
4
5
6
7
8
9
10
11
12
{
cursor: pointer;
cursor: default;
cursor: crosshair;
cursor: move;
cursor: wait;
cursor: help;
cursor: text;
cursor: progress;
cursor: auto;
cursor: e-resize;
}