当前内容所在位置(可进入专栏查看其他译好的章节内容)
- 第一章 层叠、优先级与继承(已完结)
- 1.1 层叠
- 1.2 继承
- 1.3 特殊值
- 1.4 简写属性
- 1.5 CSS 渐进式增强技术
- 1.6 本章小结
- 第二章 相对单位(已完结)
- 2.1 相对单位的威力
- 2.2 em 与 rem
- 2.3 告别像素思维
- 2.4 视口的相对单位
- 2.5 无单位的数值与行高
- 2.6 自定义属性
- 2.7 本章小结
- 第三章 文档流与盒模型
- 3.1 常规文档流(已完结)
- 3.2 盒模型 ✔️
- 3.2.1 避免使用魔数 ✔️
- 3.2.2 调整盒模型 ✔️
- 3.3.3 全局设置 border-box ✔️
- 3.3 元素的高度(精译中 ⏳)
文章目录
- 3.2 盒模型
- 3.2.1 避免使用魔数
- 3.2.2 调整盒模型
- 3.2.3 全局设置 border-box
3.2 盒模型
当前示例页需要处理的下一个问题,是主容器和社交链接区域中的内边距。当前这些区域的文字紧挨着白色背景的边缘,应该适当留白看起来才不会那么拥挤,也更方便阅读。按代码清单 3.5 所示代码更新样式。
代码清单 3.5 设置容器内边距
.main {
padding: 1em 1.5rem; /* 为容器添加内边距 */
background-color: #fff;
border-radius: 0.5em;
}
.social-links {
padding: 1em 1.5rem; /* 为容器添加内边距 */
background-color: #fff;
border-radius: 0.5em;
}
现在两个白色容器内的内容区稍微变窄了一些,留出了足够的呼吸空间。然而这样一来,正文的左侧就不再与上方标题栏内的文字在水平方向上对齐了(如图 3.6 所示):
图 3.6 设置内边距后,文字不再统一左对齐
这个问题貌似可以通过在页面标题栏添加类似的内边距来解决;但如果按照代码清单 3.6 更新样式表,会发现情况没有丝毫改变:
代码清单 3.6 给页面标题添加内边距
.page-header h1 {
max-inline-size: var(--column-width);
margin-inline: auto;
padding-inline: 1.5rem; /* 给页面标题设置相同的内边距 */
}
如果恰巧在较小的屏幕上(宽度小于约 1100px
)显示,可能看起来像是有效的;但在更宽的屏幕上,根本没有产生任何肉眼可见的变化。即使添加了内边距 padding
,标题栏内容区的宽度也没有像主容器里的那样变窄。
这都是 盒模型(box model) 的默认行为造成的。根据盒模型的设计规范,页面上的每个元素都是由四个重叠在一起的矩形所构成的。内容区(content area) 是最里面的矩形,其中包含元素的内容;内边距区(padding area) 包含 内容区 外加 所有内边距部分;同理,边框区(border area) 则是 内边距区 外加 所有边框部分;而 外边距区(margin area) 则是最外面的矩形,包含 边框区 外加 所有外边距部分。
定义
盒模型 描述了 HTML 元素的各个组成部分(内容区、内边距、边框和外边距),以及它们对元素尺寸大小的影响;这些组成要素所产生的各矩形盒将由浏览器完成布局并最终呈现到页面上。
指定元素的高度或宽度,也就设置了该元素内容区的大小;所有的内边距、边框和外边距都会添加到其外部(如图 3.7 所示):
图 3.7 盒模型的默认行为模式
这样的行为模式意味着一个宽度为 300px
、内边距为 10px
且边框宽 1px
的元素,渲染出的实际宽度为 322px
(即宽度加上左右内边距再加上左右边框)。要是单位再不一致,情况就更令人困惑了。
回到示例页面,给宽度为 1080px
的 <h1>
元素添加内边距,最终增加了其有效宽度。内边距在内容区 1080px
的外部,而正文区的宽度仍保持 1080px
。
注意
上下外边距以及上下内边距在行内元素上的行为模式略有不同。这些边距值虽然也会增加元素的高度,但并不会影响到行内元素所在容器的高度;其容器的高度是由行内元素的行高
line-height
决定的。如需变更该行为模式,行内元素须声明display: inline-block
。
Outline 轮廓 —— 另一种边框类型
与边框
border
类似,元素也可以添加一个outline
轮廓。 其行为模式很像边框,但不会增加元素尺寸,也不是盒模型的组成部分。outline
位于边框外部,与外边距重叠。它既不会改变元素的大小或位置,也不会对页面布局造成任何影响。与
border
类似,outline
属性也是outline-color
、outline-style
及outline-width
这三个属性的简写形式。例如,outline: orange solid 2px
会在元素周围添加一个2px
宽的橙色(orange)轮廓。与边框不同的是,无法为元素的每一侧设置不同的轮廓;所有四条边上的轮廓样式始终相同。在以前,轮廓的四个角总是正方形尖角,但最近部分浏览器已经更改了轮廓的行为模式,以便与元素上任何指定了border-radius
的圆角曲线相匹配。想要控制轮廓的位置,可以通过设置
outline-offset
属性来实现。其属性值为正值(如outline-offset: 3px
)时,轮廓将向外扩展,从而增加元素边框与轮廓之间的空间;为负值时,轮廓将向内收缩,使其与元素边框区域重叠。
全面理解盒模型是用好 CSS 的重中之重。内边距(padding)和边框(border)都有可能增加元素的尺寸大小——如果在这一点上认识不到位,CSS 的这些行为模式可能会让您措手不及。想要调整样式与之相适应,首要的一步是弄清为什么会发生这种情况。
3.2.1 避免使用魔数
有时遇到像这样的问题,人们往往会反复试错各种属性值来达到想要的效果,尤其是在用百分比定义大小的时候。
假设样式宽度用的是 70%
而不是 1080px
,一个天真的修复办法很可能是减少 <h1>
元素的百分比宽度;或许改成 66%
看上去还行,但这并不可靠。这里的 66%
就是一个 魔数(magic number)。您并没有使用一个理想的值,而是在样式上一顿东拼西凑肆意更改,直到改出想要的效果。
通常情况下,编程中出现魔数的做法并不可取,因为很难解释清楚该魔数为什么有效。如果不了解魔数的出处,自然也就无法预测它在不同情况下表现出的行为。也许文字在 1400px
宽的视口上能对齐,但换到更大或更小的屏幕上就不行了。尽管在开发 CSS 样式时有试错的时候,但那通常是针对与样式本质相关的选择而言的,而不是用于强行调整元素的定位布局。
取代魔数的一个替代方案,是把具体计算的麻烦交给浏览器处理。本例中,<h1>
共超宽了 3em
(考虑到左右内边距),因此可以使用 calc()
函数来减少相应的准确宽度。将宽度设置为 calc(var(--columns-width) - 3em)
正好满足需求。但还有一种更好的解决方案。
译注
“魔数”一词在上一版中译为“魔术数值”,但并未对其含义展开讨论。根据《代码大全》第二版第 12 章 12.1 小节的解释,magic number 被译为“神秘数值”,是指在程序中出现的、没有经过解释的数值字面量,如 100 或者 47524。原文摘录如下,以加深理解:
“Magic numbers are literal numbers, such as 100 or 47524, that appear in the middle of a program without explanation.”
这里之所以选用“魔数”,是因为“魔数”更常见,有点约定俗成的意味。
3.2.2 调整盒模型
盒模型的默认行为往往会导致页面元素的大小和对齐出现问题;而人们想要的效果,是希望设置的宽度能包含内边距和边框。在 CSS 中可以通过 box-sizing
属性来调整盒模型的行为。
box-sizing
的默认值为 content-box
,也就是说,指定的任何高度或宽度,其实设置的都是内容盒(content box)的尺寸大小。如果将 box-sizing
的属性值改为 border-box
,那么属性 width
、height
、inline-size
和 block-size
设置的尺寸,就是内容区、内边距和边框区域共同组合起来的尺寸,而这正是本例期望的效果。
如图 3.8 所示,左边盒模型的 box-sizing
设为了 border-box
。此时内边距不会加宽元素,而是让里面的内容区收窄;高度也是如此。左右两边的元素具有相同的宽度和高度;注意,当存在有效的内边距或边框时,具有边框盒尺寸(border box sizing)的元素将比具有内容盒尺寸(content box sizing)的元素更小。
图 3.8 边框盒尺寸(border-box sizing)改变了盒模型,从而使宽高更容易预测
如果将 <h1>
改为使用边框盒尺寸(border box sizing),其文字内容遍与下方正文区的内容对齐了(如图 3.9 所示):
图 3.9 边框盒尺寸下的文字内容左对齐效果
根据以下代码更新标题栏的盒模型设置:
代码清单 3.7 具有已更正的盒模型的标题
.page-header h1 {
box-sizing: border-box; /* 将盒模型改为边框盒尺寸 */
max-inline-size: var(--column-width);
margin-inline: auto;
padding-inline: 1.5rem;
}
对一级标题设置 box-sizing: border-box
后,其内边距的大小也计入了 1080px
的宽度内。这样标题文字就与下方的正文内容对齐了。
3.2.3 全局设置 border-box
至此,示例中的元素盒模型行为已经变得更加直观了,但其他元素肯定也有同样的问题。如果能一次性解决这个问题,并且适用于所有元素,那就再好不过了,今后就再也不必逐一考虑该如何调整了。利用通用选择器(*
)就能实现这一目标。如代码清单 3.8 所示,该选择器将对页面上的所有元素生效,同时,我还特意加上了一组对页面上所有伪元素也能生效的选择器。将以下这段代码放在示例样式表的顶部:
代码清单 3.8 通用边框框修复
*,
::before,
::after {
box-sizing: border-box; /* 将边框盒尺寸应用到页面上所有的元素及伪元素 */
}
样式生效后,height
和 width
所指定的,将始终是元素的实际高度和宽度,它们将不再受内边距的干扰。
这样,网站上的每个元素都将具有预见性更好的盒模型行为。建议每次开始新网站的开发时,都将代码清单 3.8 里的样式添加到 CSS 中;长远来看,这将省去很多麻烦。然而,对于现有样式表,尤其是已经在默认的内容盒模型下编写了大量样式的情况下,该设置可能也会带来一些新的问题。如果现有项目确实需要加上这段样式代码,请务必仔细检查是否存在任何错误。
注意
在样式表开头附近添加这段代码已是普遍做法了。从现在开始,本书中的每个示例都将假定这段
border-box
设置位于样式表的开头。