CSS是为图文信息展示而诞生的。如果让你构建一个CSS世界,要求是非常便于图片和文字的呈现,你会怎么构建?本文总结了《CSS世界》和《CSS新世界》中张鑫旭老师对CSS世界的理解。从CSS世界出发,重新去理解什么是盒模型?布局怎么影响一个个盒子?以及为什么要这样设计?
1. CSS世界
在CSS世界中,HTML是魔法石,选择器就是选择法器,CSS属性就是魔法师,CSS各种属性值就是魔法师的魔法技能,浏览器就是他们所在的"王国"。CSS 作用于 HTML 元素上,各种布局影响了元素的展示,从而构建出一个个丰富的页面。
2. 盒模型
每个HTML元素都有外在盒子和内在盒子。
- 外在盒子:负责元素是可以一行显示,还是只能换行显示
- 内在盒子:负责宽高、内容怎么呈现。width/height作用在内在盒子上
元素的 display 属性可以理解为 display: 外在盒子-内在盒子。 如display: inline-block
,它的外在是inline盒子,一行显示。内在是block盒子,允许设置宽高。
2.1 外在盒子
外在盒子可以分成这几类:块级盒子,内联盒子,特殊盒子,表格盒子
外在盒子 | display | display: 外在-内在 | 表现 | 常见元素 |
---|---|---|---|---|
块级盒子 | display: block | display: block-block | 换行显示 | <div><p><h1> |
内联盒子 | display: inline-block | display: inline-block | 一行显示,可以设置宽高 | 手动设置css属性 |
内联盒子 | display: inline | display: inline-inline | 一行显示,无法设置宽高 | <span><a><img><input> |
特殊盒子 | list-item | 让元素额外显示符号 | <ul><li> |
|
表格盒子 | display: table | display: block-table | 换行显示 | <table> |
表格盒子 | display: inline-table | display: inline-table | 和文字图片一行显示 | 手动设置css属性 |
display: inline-table示例
2.2 内在盒子
内在盒子,也叫容器盒子。width/height会作用在容器盒子上。
内在盒子分成了4个盒子,分别是content box、padding box、border box和margin box。
-
box-sizing
是改变 width / height 的作用细节
- content-box 让宽度作用在content box上(默认值)
- border-box 让宽度作用在border box上(width = content + padding + border)。对应JS的 offsetWidth, offsetHeight
- JS中的 clientWidth, clientHeight 对应padding box的尺寸
-
width的默认值是auto,如何理解auto?
- 充分利用空间:
<div>、<p>
元素的宽度默认是100%于父级容器的。display: block 的元素不用额外加 width: 100% - 收缩与包裹:
inline-block, table
会包裹元素内容至合适宽度,理解为fit-content
- 收缩到最小:当空间不够的时候,如很多列的table有的列会收缩为一行,理解为
min-content
- 超出容器限制:一般子元素都不会主动超过父元素的容器宽度,除非内容是很长的连续英文和数字
- 遵循无宽度准则,因为元素一旦设置了宽度,就失去了流动性
- 充分利用空间:
-
height: 100%
注意,父级没有具体高度值的时候,
height: 100%
是无效的。
3. 内联元素与流
有了盒子,如何排列盒子?这个时候就要引出"流"的概念。
"流"是CSS世界中一种定位和布局机制,它是一条引导元素排列和定位的、看不见的"水流"。流作用于外在盒子,块级盒子负责结构,内联盒子接管内容。CSS世界是面向图文混排的,也就是为内联元素设计的。
内联布局比较重要的两个问题:对齐和行高。当一堆内联元素放在同一行:
- 它们在行上如何对齐?
- 已知 display: inline 元素无法设置width/height,那应该怎样去调整它们的高度?
3.1 vertical-align 基线
先回答第一个问题,如何对齐? 在各种内联相关模型中,凡是涉及垂直方向的排版或者对齐的,都离不开最基本的基线(baseline)。例如,line-height行高的定义就是两基线的间距,vertical-align的默认值就是基线。
什么是基线?
vertical-align: baseline
:基线,字母x的下边缘线。内联元素默认是基线对齐的
什么是中线?
vertical-align: middle
:中线,基线往上 1/2 * x-height 的高度,近似x的交叉点位置。vertical-align:middle并不是绝对的垂直居中对齐。因为大部分字体都要往下偏一点。x-height
:指小写字母x的高度。
3.2 line-height 占据高度
vertical-align提供了内联元素垂直对齐的功能。 再来回答第二个问题,已知 display: inline 元素无法设置width/height,那应该怎样去调整它们的高度?
此时,有一个空的<div>
,高度是0,我们在里面写上几个文字,<div>
高度就有了,这个高度由何而来,或者说是由哪个CSS属性决定的?
不少人会认为<div>
高度是由里面的文字撑开的,也就是font-size决定的,但本质上是由line-height属性全权决定的。line-height如何通过改变行距实现文字排版?
line-height: 2
: 两行文字中间的间隙一个文字尺寸大小
line-height: 1
: 两行文字会紧密依偎在一起
line-height: 0.5
: 两行文字就是重叠纠缠在一起
3.3 空白幽灵节点
line-height会影响内联元素(文字)的占据高度,那替换元素<img>
和 inline-block 内联块级元素呢?
下面例子中, box里只有一个img元素。<img>
元素的高度是128px, box元素的高度是256px。line-height把图片占据的高度变高了吗?
html
.box {
line-height: 256px;
}
<div class="box">
<img src="1.jpg" height="128">
</div>
答案是:
line-height不能影响<img>
的占据高度。而是把"幽灵空白节点"的高度变高了。<div><img /></div>
会在<div>
内构成一个"行框盒子"。在HTML5文档模式下,每一个"行框盒子"的前面都有一个宽度为0的"幽灵空白节点",其内联特性表现和普通字符一模一样,所以,这里的容器高度会等于line-height设置的属性值256px。
4. 流的破坏(float, absolute)
至此,内联布局、流布局解决了大部分图文排版的问题。但它们总是方方正正的,有时候我们希望有一些特殊的布局,如文字环绕效果,或者元素固定在某个位置,要实现这样的效果,正常流就有些捉襟见肘。因此,CSS使用float
, absolute
通过破坏正常流来实现一些特殊的样式表现。
-
float
浮动的本质就是为了实现文字环绕图片效果。float 通过破坏正常CSS流实现CSS环绕,脱离了正常流,让父元素高度坍塌。
-
absolute
同样具有破坏性的是
position: absolute | fixed | relative
实现精确定位
5. 传统CSS布局
总结一下上面的内容,CSS 2.1 定义了4种布局方式:
- 块布局:元素以 display: block 的方式布局,负责布局结构
- 内联布局:元素以 display: inline 的方式布局,负责展示文本内容
- 表格布局:元素以 display: table(inline-table) 布局,用于布局二维表格数据
- 定位布局:元素以 position: absolute 定位, 不考虑文档中其他元素,用于精确定位
这4种布局都是为图文信息展示而生。但随着Web页面越来越复杂,二维布局的需求越来越多,用这4种方法实现一个左侧栏右内容的布局,我们会:
display:inline-block
让div元素变成内联块position: absolute
精确定位加上margin-left: -200px
float
实现
但它们都不是专门为二维结构设计的。在这种背景下,全新的CSS布局模式出现。它们就是 flex 弹性布局和 grid 网格布局。 这两种布局专门为现代Web应用设计,代码简单,样式丰富,逐渐成为主流的布局方式。
6. flex 弹性布局
flex 弹性布局通过流向控制、对齐设置、空间分配实现布局效果。
6.1 流向控制
flex-direction
布局方向 控制子项是从左往右、从右往左,从上往下,从下往上排列flex-wrap
换行表现 控制子项是单行显示还是换行显示- nowrap:单行显示,且不换行,可能会出现子项宽度溢出
- wrap:当容器宽度不足时,子项会换行显示
6.2 对齐设置
justify-content
水平对齐align-items
垂直对齐
6.3 空间分配
我们可以把弹性布局的空间分配看成分配家产
flex-basis
分配家产的基础数量 相当于widthflex-grow
家产有富余时如何分配 默认值是0,表示多余空间不分配。所有剩余空间总量是1,如果flex-grow
是0.3,表示分配剩余空间 * 0.3。如果大于1,则独享所有空间。如果子项都设置了flex-grow,总数>1,则按值/总数来分配flex-shrink
家产不足时如何收缩 默认值是1,如果空间不足,子项等比例收缩,填满flex容器。一个子项的flex-shrink为0,其余为默认1,那么空间不足时,该项目不收缩,其他都收缩。
flex 是 flex-grow, flex-shrink, flex-basis 三个属性的缩写
6.4 顺序控制
通过 order 改变子项的排列顺序,数值越小,排列越靠前。默认为0
7. grid 网格布局
网格布局(Grid)是很强大的 CSS 布局方案。 Grid 布局将容器划分出"行"和"列",产生一个个单元格,然后指定"项目所在"的单元格,从而做出各种各样的布局。
7.1 划分单元格
grid-template-columns
定义每一列的列宽grid-template-rows
定义每一行的行高grid-gap: 20px 20px
定义行与行之间的间距,列与列的间距
7.2 指定区域
grid-template-areas
定义区域,多个单元格可以合并区域
css
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-template-areas: 'a a a'
'b b b'
'c c c';
}