盒模型基本概念
每一个元素在页面中都会生成一个矩形区域,这个矩形区域就是盒子,换句话说,每一个元素都是以一个盒子的样式显示在网页中的。
盒子类型有以下几种
- 行盒:display 属性为 inline
- 块盒:display 属性为 block
行盒和块盒最显而易见的区别就是,块盒会独占一行,行盒不换行。
行级元素和块级元素本来是一个 HTML 的概念,但在 HTML 5 之后废除了这个叫法,因为行级元素和块级元素涉及到显示的范畴,所以就拿到了 CSS 的盒模型中,任何元素既可以设置为行盒也可以设置为块盒,所以并没有行级元素和块级元素这一概念。
通过设置 display 属性,可以修改元素的盒模型,在标准中,display 的默认值为 inline,像 p 元素这种看起来默认是块盒的元素其本质上是浏览器的样式表修改了 display 属性,如果这里有疑问,可以回顾前置知识。

这里的 user agent stylesheet 就是浏览器默认样式表
这里浏览器让 p 元素默认为块盒,使其独占一行,我们可以使用 display:inline 修改
html
<p style="display:inline">this is a p</p>
<p style="display:inline">this is a p</p>此时这两个 p 元素不再独占一行,而是在同一行显示。
在这里我们可以看到,在浏览器默认样式表中,会把一些元素设置为块盒,比如这里的 p 元素,以下是常见的会被浏览器设置为块盒的元素
- 容器元素
- h1~h6
- p 以下是常见的不会被浏览器设置为块盒的元素
- span
- a
- img
- video
- audio
盒模型的四个区域
不管是行盒还是块盒,盒子都由这几部分组成,从内到位分别为
- 内容 content
- 填充 padding
- 边框 border
- 外边距 margin

我们可以在 chrome 中的调试工具中看到这个盒模型。
边框和内容我想不需要解释,在内容和边框中间会有一些距离,这个距离叫内边距,也就是填充,在边框外面和其他盒子之间会有一些距离,这就叫外边距。
前面我们提到的 width 和 height 设置的就是内容部分的宽高,这里我们可以试验一下
html
<style>
p {
height: 100px;
width: 120px;
}
</style>
<p>this is a p</p>
此时我们就可以看到盒模型的内容区域,也就是蓝色区域的宽高被设置为了 120*100
内容盒:内容部分这个矩形区域指的就是内容盒 (content-box)
填充指的是盒子边框到盒子内容的距离,我们可以通过 padding-left, padding-right, padding-top, padding-bottom 设置
css
p {
height: 100px;
width: 120px;
background-color: red;
padding-top: 50px;
padding-left: 20px;
}此时 p 元素呈现以下形态

很显然,内容区域,即 this is a p 这个字,离盒子边框就有了一定距离。
可以使用 padding 一个属性简写,书写格式为:
padding: 上 右 下 左;,按照这个格式来一次性写出四个内边距的值。我们称这种方便我们书写的属性为速写属性。
若padding属性只写一个值,则四个内边距都会被设为这个值。如果只写三个值,那么会设置上、右、下的值,左内边距使用右内边距的值。如果只写两个值,会设置上、右内边距的值,下内边距使用上内边距的值,左内边距使用右内边距的值。
填充区+内容区叫做填充盒(padding-box)
边框区域包含三个信息
- 边框样式 (border-style)
- 边框粗细 (border-width)
- 边框颜色 (border-color)
css
p {
height: 100px;
width: 120px;
background-color: lightblue;
padding-top: 50px;
padding-left: 20px;
border-style: solid;
border-width: 5px;
border-color: #f40;
}
p 元素显示效果如图所示,蓝色区域为内边距,淘宝红区域为边框,分别使用三个属性给边框设置了样式,将边框颜色设置为了淘宝红,宽度设置为了 5 像素,将样式设置为了实线。
边框样式默认为 none,所以如果不设置边框样式,边框是不会显示的
如果不设置边框颜色,那么边框颜色默认为字体颜色
这里用到的三个设置边框的属性均为速写属性,可以在 chrome 调试工具内看到

所以其实我们可以给单独一个方向的边框设置样式
如果这个边框四个方向上都是一样的样式,我们一般会使用 border 这个速写属性,这个属性可以统一设置四个方向的边框样式,border 属性需要给三个值,分别为样式,颜色和粗细
css
p{
border:1px solid #fff;
}如此便完成了边框的设置,这三个属性值的书写顺序无关紧要。

可以看到,这个 border 属性其实是由这么多个属性共同组成的。
边框+内边距+内容=边框盒 (border-box)
以上我们介绍了 border-box、padding-box 、content-box,除此之外这里还定义一个 margin-box 的概念,margin-box 即 margin+border+padding+content 的区域,margin 由于是盒子的外部区域,所以并没有太多的实际用途,这里只作为一个术语了解即可。
外边距就是这个盒子边框和其他盒子的距离

我们可以看到,当我把上面的外边距设置为了 50 像素后,两个 p 元素中间的距离就变成了 50 像素

设置为 150 像素后,距离进一步增加

这里可以看到,黄色区域就是外边距,上外边距是 150 像素,左外边距是 60 像素,这里的下外边距的 16 像素其实是浏览器的默认样式表设置的,不去管他,但左边距明明就没有,盒模型上显示的也是没有,为什么会有这么大的区域呢,这里的问题放在布局部分 。
以上都是基于块盒进行介绍,行盒也是由 content、padding、border、margin 组成的。
行盒模型
上面介绍盒模型均以块盒为例,行盒的盒模型和块盒有一定的差异,这好像是个废话,如果没有差异就不会区分行盒和块盒了,本节主要介绍差异部分。

在这个页面中,上面的元素为 p 元素,这是一个块盒,下面的元素为行元素,这是一个行盒,这就很有意思了,第一个块盒元素,会占满整行,当出现换行的时候会自动改变高度把内容全部包进去,但是块盒的元素就不一样了,一行放不下的时候会直接截断,然后继续接在第二行显示。

我们会发现,行盒其实是跟着内容走的,如果我们在后面加一个 span 元素,它也不会独占一行,而是紧接在上一个 span 后面。 除了这个神奇的特点以外,我们可以给行盒设置一个宽度和高度

这里 p 元素生效了,但是 span 元素根本不受影响,我们无法再行盒里面设置内容区的宽高,行盒的尺寸应该由内容本身决定,这一点和块盒不一样,如果我们要调整行盒内容区的宽高,我们可以考虑设置字体的尺寸间接调整内容区的尺寸。
接下来看一下内边距区域

左右内边距,这个其实和块盒是一样的。

上下就产生区别了,读者会发现,当我们设置上下边距之后,它的边框变得很乱,这是因为行盒的上下内边距并不占据空间,也就是说不会把其他元素挤下去。可以对比一下左右边距,当我们设置左右边距的时候,后面的那一个 span 元素是不是被往后挤了,但是设置上下内边距的时候并不会将其他元素挤开,所以对于行盒垂直方向的内边距,只会影响背景,不占据实际空间。
同样,对于边框,设置边框的时候,会将左右的元素往边上挤开,但是并不会将上下元素挤开,只能看到边框的效果。
CSS 认为行盒内容的高度应该取决于行高,不应该取决于内边距或者边框,所以设置上下内边距和宽高只有显示效果,并不会占用实际的空间。
同样,对于外边距也是水平生效,垂直完全失效。
总结行盒的显著特点
- 盒子沿着内容延伸
- 行盒无法设置宽高,调整行盒的宽高应该使用和字体相关的属性进行调整
- 水平方向内边距有效,垂直方向内边距只有显示效果,不占用实际空间
- 水平方向边框有效,垂直方向的边框只有显示效果,不占据实际空间
- 水平方向外边距有效,垂直方向的外边距完全无效
行块盒
行块盒的 display 属性为 inline-block
行块盒既又行盒的特点,又有块盒的特点,以下罗列
- 不独占一行
- 盒模型的所有尺寸,包括 padding,margin,border,width,height 均有效

可以看到,它长得也和块盒一样,也是一个方方正正的,只不过它是水平排列的而已,所以读者其实可以理解为是一个不独占一行的块盒。
行块盒最常见的应用场景是分页,但请读着注意,如果我要将几个盒子横过来排列,一般不使用行块盒,会使用后面的浮动,或者用 CSS 3 更为先进的弹性布局。
读者可能会发现,在上图中,设置成 inline-block 的两个 span 元素中间有间隙,这个间隙就来自于空白折叠,空白折叠发生在行盒(行块盒)内部盒行盒(行块盒)之间,两个 span 元素之间有空白字符,它会折叠成一个空格,我们无法将这个空格消去,这也是不推荐使用行块盒实现横向排列的原因之一。
可替换元素和非可替换元素
非可替换元素:页面上显示的结果取决于元素的内容。
可替换元素:页面上显示的结果取决于元素的属性。
可能这样的概念比较抽象,大多数元素其实都是非可替换元素,比如 a 元素,比如 p 元素,这些都是标签内部写什么字就显示什么字。但是像 img 元素这种,显示的内容完全取决于属性,相当于就是用属性去替换了元素的内容,这种元素就是可替换元素。
绝大多数可替换元素都是行盒,这一点可以在浏览器的控制台验证,例如 img 元素就是一个行盒,但 img 元素是可以设置宽高的,所以对于可替换元素而言,虽然它是行盒,但依旧可以设置宽高,并且 padding,margin 这些完全生效,所以我们可以简单理解为这是一个行块盒。
对于可替换元素来说,CSS 不能完全控制其样式。
img 元素的细节

当我将 img 元素设置宽高的时候,图片被强制拉伸了,我们其实可以通过 object-fit 修改其适应方式,也就是当盒子尺寸和图片尺寸不合适的时候,应该怎么去处理。
object-fit 的常见取值主要有以下几个
- contain:图片比例不变,保证图片的所有内容都要包含在盒子内
- cover:图片比例不变,保证占满整个盒子
- fill:强制拉伸图片填满整个区域,这是默认值 img 元素的另一个细节是,对于宽高属性,如果我只设置其中一个,另一个会按照图片比例自动设置。
盒模型的一些细节
宽高范围
默认情况下,我们设置的宽高,也就是 width 和 height 属性是设置内容区域的宽高,也就是内容盒的宽高。但很多时候这并不实用,这里用下面的例子举例

如果我要把这个按钮还原为网页,红色区域为边框盒,蓝色区域为内容盒,我们量尺寸的时候很自然地是去量红色区域,而非蓝色区域,并且内容区域其实并不是很好量宽高,因为边框一般都有明显的分界线,但内容没有,其实设计稿一般也是边框盒的宽高,所以其实我们更希望直接让 width 和 height 为红色区域,也就是整个盒子的宽高。
在以前,我们要解决这个问题,只有精确计算,比如内容距离左边框 10 像素,整个盒子宽为 100 像素,那就得把内容设置为 90 像素,然后左内边距设置为 10 像素,很显然这并不是很理想,徒增开发难度,我们其实更愿意让 width 和 height 设置为整个盒子的宽高,也就是边框盒的宽高,CSS 3 提供了这个解决方案,CSS 3 提供了 box-sizing 属性,这个属性就可以规定 width 和 height 是作用到谁身上,默认是 content-box,即内容盒,我们希望让它作用域边框盒,可将它设置为 border-box。


可以通过以上例子感受一下二者区别,关注左下角盒模型示意图的尺寸。
box-sizing只有两个取值,即content-box和border-box
背景覆盖范围
默认情况下,背景覆盖边框盒,可以用下面的例子验证

很显然,我们将 border 设置为打点之后,可以看到,不仅是内容区域和内边距区域,就连边框部分也被覆盖上了背景颜色,在一些场景下,我们可能并不希望边框覆盖这么多的范围,比如我们希望背景只作用于内边距和内容,或者只作用于内容区,这个时候就可以使用 background-clip 属性进行修改。

当我们将其设置为 content-box 的时候,背景颜色就只会作用于内容盒,这个属性在一些特定的场景下非常有用。
溢出处理
一般来说,如果我们没有设置宽度和高度,内容是不会溢出的,盒子尺寸会随着内容自动增加

但如果我设置了宽高,内容就可能溢出

内容溢出边框盒之后的处理方式可以有很多种,默认情况下,溢出部分是可见 (visible)的,我们可以使用 overflow 对溢出的处理方式进行设置,比如我可以让它溢出直接隐藏

我们也可以让它溢出后生成一个滚动条

overflow 是一个速写属性,可以使用单独的属性对水平和垂直的溢出做不同的处理 
当我们将 overflow 设置为 scroll ,滚动条始终出现,哪怕没有溢出也会出现滚动条。如果我们希望它溢出的时候才出现滚动条,如果不溢出就隐藏滚滚动条,我们就可以将 overflow 设置为 auto

设置为 auto 之后,只有当溢出的时候才会出现滚动条,没有溢出就不会出现滚动条。
断词规则
在前面的溢出规则的案例中,我们其实可以发现,当宽度显示不下的时候,文本会自动换行,请读者细心观察一下,这里的换行是有一定规律的,它一个单词不可能同时出现在两行,发现了吧,也就是说它会将这一堆字母按照空格去截断成一个一个单词,按照单词为单位进行的换行处理,这就可以保证一个单词永远也不可能出现在两行。
这种按照空格去截断为一个一个单词,就称为断词规则,顾名思义嘛,就是将文本截断为一个一个词语的规则。
一个元素里面内容的断词规则可以根据 word-break 属性来设置,默认值为 normal,normal 规则为对于中文、韩文、日文,会从文字位置截断,其他字符按照单词位置进行截断,或者说按照空格进行截断。
我们可以修改为 break-all,这可以让所有字符都在文字处进行截断。

可以看到,此时,如果一个单词这一行显示不出来,还是会直接按照字母往后写,并不会按照单词进行整体换行。
我们也可以设置为 keep-all,也就是都在单词之间进行截断,也就是按照空格截断。

此时中文就会出现问题,由于没有空格,它会认为这一大坨都是一个单词,它就不会产生截断,全部显示在这一行。
一般来说 normal 是非常合理的,所以我们不怎么会去修改这个属性
这个属性还有一个值为break-word,但已被弃用,所以这里不介绍
一般来说,单词之间才允许换行,我们可以使用
overflow-wrap属性来设置,这个属性默认值为normal,也就是在单词之间才允许换行,我们可以设置其为anywhere,就可以允许在任何地方进行换行。除吃之外,如果我要阻止换行这个行为,就可以使用white-space:nowrap;
空白处理
单行文本溢出打点
css
li{
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}多行文本溢出打点
css
li{
overflow: hidden;
text-overflow: ellipsis;
/*将对象作为弹性伸缩盒子模型显示*/
display: -webkit-box;
/*用来限制在一个块元素中显示的文本的行数*/
-webkit-line-clamp:3;
/*设置伸缩盒对象的子元素的排列方式*/
-webkit-box-orient: vertical;
}这里的 white-space 表示对空白字符的处理,设置为 nowrap 指的是空白折叠依然会发生,但不会换行,如果设置为 pre,就会保留元素中的所有空白字符,pre 元素就是通过这个属性实现的效果。
white-space 的默认属性为 normal,就是普通情况,普通情况下会进行空白折叠。