深入解析CSS

盒模型和 border-box

盒模型指的是网页元素的结构。当指定一个元素的宽度或高度时,便设置了元素内容的尺寸——任何内边距(padding)、边框(border)、外边距(margin)都会基于它叠加。

给元素设置 box-sizing: border-box会改变盒模型,使其获得更好的可预测性。指定宽度或高度时,会设置整个元素尺寸,包括内边距和边框。

基础回顾

层叠

层叠指的就是这一系列规则。它决定了如何解决冲突,是CSS语言的基础。

层叠会依据三种条件解决冲突。

  1. 样式表的来源
  2. 选择器优先级
  3. 源码顺序

层叠的规则是按照这种顺序来考虑的。

以下是CSS中的一行。它被称作一个声明。该声明由一个属性(color)和一个值(black)组成。

1
color : black;

包含在大括号内的一组声明被称作一个声明块。声明块前面有一个选择器。

1
2
3
4
body{
color : black;
font-family: Helvetica;
}

选择器和声明块一起组成了规则集(ruleset)。
@规则(at-rules)是指用“@”符号开头的语法。比如@import规则或者@media查询。

优先级按照由高到低排列如下所示

  • 作者的!important(带!important的样式)
  • 作者(编写的样式)
  • 用户代理(浏览器默认样式)

浏览器将优先级分为两部分:HTML的行内样式和选择器的样式。

用 HTML 的 style 属性写样式,这个声明只会作用于当前元素。实际上行内元素属于“带作用域的”声明,它会覆盖任何来自样式表或者<style>标签的样式。行内样式没有选择器,因为它们直接作用于所在的元素。

选择器优先级的准确规则如下。

  • 如果选择器的ID数量更多,则它会胜出(即它更明确)。
  • 如果ID数量一致,那么拥有最多类的选择器胜出。
  • 如果ID数量一致,那么拥有最多类的选择器胜出。

注意:伪类选择器(如:hover)和属性选择器(如[type="input"])与一个类选择器的优先级相同。通用选择器(*)和组合器(>、+、~)对优先级没有影响。

优先级标记

通常最好让优先级尽可能低,这样当需要覆盖一些样式时,才能有选择空间。

可以通过控制源码顺序,来给特殊链接添加样式。如果两个冲突选择器的优先级相同,则出现得较晚的那个胜出。

链接加样式要按照一定的顺序书写选择器。这是因为源码顺序影响了层叠。

1
2
3
4
5
6
7
8
9
10
11
12
13
a:link{
color: blue;
text-decoration: none;
}
a:visited{
color: purple;
}
a:hover{
text-decoration: underline;
}
a:active{
color: red;
}

优先级相同时,后出现的样式会覆盖先出现的样式。
如果一个元素同时处于两个或者更多状态,最后一个状态就能覆盖其他状态。如果用户将鼠标悬停在一个访问过的链接上,悬停效果会生效。如果用户在鼠标悬停时激活了链接(即点击了它),激活的样式会生效。

这个顺序的记忆口诀是“LoVe/HAte”。即link(链接)、visited(访问)、hover(悬停)、active(激活)。

注意,如果将一个选择器的优先级改得跟其他的选择器不一样,这个规则就会遭到破坏,可能会带来意想不到的结果。

如果一个声明在层叠中“胜出”,它就被称作一个层叠值。元素的每个属性最多只有一个层叠值。如果CSS为同一个属性指定了不同的值,层叠最终会选择一个值来渲染元素,这就是层叠值。

层叠值——作为层叠结果,应用到一个元素上的特定属性的值。

两条经验法则

  • 在选择器中不要使用ID。就算只用一个ID,也会大幅提升优先级。
  • 不要使用!important。
关于重要性的一个重要提醒 当创建一个用于分发的JavaScript模块(比如NPM包)时, 正确的做法是在包里包含一个样式表。如果组件需要频繁修改样式,通常最好用JavaScript给元素添加或者移除类。这样用户就可以在使用这份样式表的同时,在不引入优先级竞赛的前提下,按照自己的喜好选择编辑其中的样式。

继承

默认情况下,只有特定的一些属性能被继承,它们主要是跟

文本相关的属性:color、font、font-family、font-size、font-weight、font-variant、font-style、line-height、letter-spacing、text-align、text-indent、text-transform、white-space以及word-spacing。

列表属性:list-style、list-style-type、list-style-position以及list-style-image。表格的边框属性border-collapse和border-spacing也能被继承。

注意,这些属性控制的是表格的边框行为,而不是常用于指定非表格元素边框的属性。

使用开发者工具是最好的追踪方式

特殊值

使用inherit关键字

想用继承代替一个层叠值,可以用inherit关键字,可以用它来覆盖另一个值,这样该元素就会继承其父元素的值。

initial关键字

撤销作用于某个元素的样式,可以用initial关键字来实现。

说明声明display: initial等价于display: inline。不管应用于哪种类型的元素,它都不会等于display: block。这是因为initial重置为属性的初始值,而不是元素的初始值。inline才是display属性的初始值。

简写属性

简写属性会默默覆盖其他样式

避免在<body>元素的通用样式以外使用font。

总结

  • 控制选择器的优先级。
  • 不要混淆层叠和继承。
  • 某些属性会被继承,包括文本、列表、表格边框相关的属性。
  • 不要混淆initial和auto值。
  • 简写属性要注意TRouBLe的顺序,避免踩坑。

相对单位

em

CSS为网页带来了后期绑定(late-binding)的样式:直到内容和样式都完成了,二者才会结合起来。

响应式——在CSS中指的是样式能够根据浏览器窗口的大小有不同的“响应”。

96px通常等于一个物理英寸的大小。

em 是最常见的相对长度单位,适合基于特定的字号进行排版。在CSS中,1em等于当前元素的字号,其准确值取决于作用的元素。

这里设置内边距的值为1em。浏览器将其乘以字号,最终渲染为16px。这一点很重要:浏览器会根据相对单位的值计算出绝对值,称作计算值(computedvalue)。

em 的好处。可以定义一个元素的大小,然后只需要改变字号就能整体缩放元素。

提示如果知道字号的像素值,但是想用em声明,可以用一个简单的公式换算:用想要的像素大小除以父级(继承)的像素字号。比如,想要一个10px的字体,元素继承的字体是12px,则计算结果是10/12 = 0.8333em。如果想要一个16px的字体,父级字号为12px,则计算结果是16/12 = 1.3333em。

对大多数浏览器来说,默认的字号为16px。准确地说,medium关键字的值是16px。

rem

当浏览器解析HTML文档时,会在内存里将页面的所有元素表示为DOM(文档对象模型)。它是一个树结构,其中每个元素都由一个节点表示。<html>元素是顶级(根)节点。它下面是子节点,<head><body>。再下面是逐级嵌套的后代节点。

根节点有一个伪类选择器(:root),可以用来选中它自己。这等价于类型选择器html,但是html的优先级相当于一个类名,而不是一个标签。

rem是root em的缩写。rem不是相对于当前元素,而是相对于根元素的单位。

我一般会用rem设置字号,用px设置边框,用em设置其他大部分属性,尤其是内边距、外边距和圆角(不过我有时用百分比设置容器宽度)。

提示拿不准的时候,用rem设置字号,用px设置边框,用em设置其他大部分属性。

将浏览器的默认字号16px 缩小为10px的缺点:

  • 我们被迫写很多重复的代码。10px对大部分文字来说太小了,所以需要覆盖它,最后就得给段落设置1.4rem,给侧边栏设置1.4rem,给导航链接设置1.4rem,等等。这样一来,代码容易出错的地方更多;当需要修改代码时,要改动的地方更多;样式表的体积也更大。
  • 这种做法的本质还是像素思维。虽然在代码里写的是1.4rem,但是在心里仍然想着“14像素”。在响应式网页中,需要习惯“模糊”值。1.2em到底是多少像素并不重要,重点是它比继承的字号要稍微大一点。如果在屏幕上的效果不理想,就调整它的值,反复试验。这种方式同样适用于像素值。(在第13章中,我们将进一步研究具体规则来改进这种方法。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 设置真正的默认字号 */
:root { /* 使用 HTML 选择器 */
font-size: 0.875em; /*14/16(理想的px/继承px) = 0.875*/
}

.panel {
padding: 1em;
border-radius: 0.5em;
border: 1px solid #999;
}

.pannel > h2 {
margin-top: 0; /* 将多余空间移除 */
font-size: 0.8rem;
font-weight: bold;
text-transform: uppercase;
}
1
2
3
4
5
6
7
8
<div class="panel">
<h2>ssss</h2>
<div class="panel-body">
fjkdsljfkjsd;fdsklf
<a href="/batch-size">smalljfjdslk</a>
fsdlfk;
</div>
</div>

媒体查询,即@media规则,可以指定某种屏幕尺寸或者媒体类型(比如,打印机或者屏幕)下的样式。这是响应式设计的关键部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
:root {
font-size: 0.75em;
}

@media (min-width: 800px) {
:root {
font-size: 0.875em;
}
}

@media (min-width: 1200px) {
:root {
font-size: 1em;
}
}

缩放单个组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.panel {
font-size: 1rem;
padding: 1em;
border: 1px solid #999;
border-radius: 0.5em;
}

.panel > h2{
margin-top: 0;
font-size: 0.8em;
font-weight: bold;
text-transform: uppercase;
}

/* 放大 */
.panel.large {
font-size: 1.2rem;
}

视口——浏览器窗口里网页可见部分的边框区域。它不包括浏览器的地址栏、工具栏、状态栏。

  • vh:视口高度的1/100。
  • vw:视口宽度的1/100。
  • vmin:视口宽、高中较小的一方的1/100(IE9中叫vm,而不是vmin)。
  • vmax:视口宽、高中较大的一方的1/100(本书写作时IE和Edge均不支持vmax)

比如,50vw等于视口宽度的一半,25vh等于视口高度的25%。vmin取决于宽和高中较小的一方,这可以保证元素在屏幕方向变化时适应屏幕。在横屏时,vmin取决于高度;在竖屏时,则取决于宽度。

视口相对长度非常适合展示一个填满屏幕的大图。我们可以将图片放在一个很长的容器里,然后设置图片的高度为100vh,让它等于视口的高度

提示相对视口的单位对大部分浏览器而言是较新的特性,因此当你将它跟其他样式结合使用时,会有一些奇怪的bug。可在Can I Use网站中检索Viewportunits: vw, vh, vmin, vmax中的“Known Issues”。

使用vw定义字号,这样做的好处在于元素能够在这两种大小之间平滑地过渡,这意味着不会在某个断点突然改变。当视口大小改变时,元素会逐渐过渡。

使用calc()定义字号,加号和减号两边必须有空白。

1
2
3
:root {
font-size: calc(0.5em + 1vw);
}

我们不用媒体查询就实现了大部分的响应式策略。省掉三四个硬编码的断点,网页上的内容也能根据视口流畅地缩放。

无单位的数值和行高

有些属性允许无单位的值(即一个不指定单位的数)。支持这种值的属性包括line-height、z-index、font-weight(700等于bold,400等于normal,等等)

有些属性允许无单位的值(即一个不指定单位的数)。支持这种值的属性包括line-height、z-index、font-weight(700等于bold,400等于normal,等等)。

怪异特性:当一个元素的值定义为长度(px、em、rem,等等)时,子元素会继承它的计算值。当使用em等单位定义行高时,它们的值是计算值,传递到了任何继承子元素上。如果子元素有不同的字号,并且继承了line-height属性,就会造成意想不到的结果,比如文字重叠。

长度——一种用于测量距离的CSS值的正式称谓。它由一个数值和一个单位组成,比如5px。长度有两种类型:绝对长度和相对长度。百分比类似于长度,但是严格来讲,它不是长度。

使用无单位的数值时,继承的是声明值,即在每个继承子元素上会重新算它的计算值。这样得到的结果几乎总是我们想要的。


相关资料
深入解析CSS代码库

作者

Fallen-down

发布于

2020-06-07

更新于

2020-10-17

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.