CSS 之布局

布局是 CSS 中重要的一环,它能让我们能够准确的控制每个元素在页面中的位置并创造出具有魅力的页面。

1. 一些基本概念

在学习布局之前,需要了解一些有关布局的基本概念,有助于帮我们更好地理解布局的内容。
① 包含块:简单来说就是定位参考框,或者定位坐标参考系,元素一旦定义了定位显示(相对、绝对、固定)都具有包含块性质,它所包含的定位元素都将以该包含块为坐标系进行定位和调整。

2. 浮动

CSS 的 float 属性指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它。

(1) 浮动元素有以下几个特点:
① 浮动元素会从正常的文档流中删除,不过还是会影响布局 —— 其他内容会「环绕」浮动元素。
② 浮动元素周围的外边距不会合并。
③ 如果浮动一个非替换元素,则必须为该元素声明一个 width。否则,根据 CSS 规则,元素宽度会趋于 0。
④ 浮动元素的包含块是最近的块级祖先元素。
⑤ 浮动元素会生成一个块级框,而无论这个元素本身是什么。

(2) 有着一系列的特定规则控制着浮动元素的摆放,规则如下(带圆圈的数字显示了元素在源文档的位置,有数字的框显示了浮动后的位置和大小):
① 浮动元素的左(或右)外边界不能超出其包含块的左(或右)内边界。

② 浮动元素的左(或右)外边界必须是源文档中之前出现的左浮动(或右浮动)元素的右(左)外边界,除非后出现浮动元素的顶端在先出现浮动元素的底端下面。这条规则防止浮动元素彼此「覆盖」。

③ 左浮动元素的右外边界不会在其右边右浮动元素的左外边界的右边。一个右浮动元素的左外边界不会在其左边任何左浮动元素的右外边界的左边。这条规则可以防止浮动元素相互重叠。

④ 一个浮动元素的顶端不能比其父元素的内顶端高。如果一个浮动元素在两个合并外边距之间,放置这个浮动元素时就好像在两个元素之间有一个块级父元素。

⑤ 浮动元素的顶端不能比之前所有浮动元素或块级元素的顶端更高。

⑥ 如果源文档中一个浮动元素之前出现另一个元素,浮动元素的顶端不能比包含该元素所生成框的任何行框的顶端更高。

⑦ 左(或右)浮动元素的左边(右边)有另一个浮动元素,前者的右边界不能在其包含块的右(左)边界的右边(左边)。

⑧ 浮动元素必须尽可能高地放置。

⑨ 左浮动元素必须向左尽可能远,右浮动元素则必须向右尽可能远。位置越高,就会像右或向左浮动得越远。

(3) 前面的浮动规则只是处理了浮动元素和其父元素的左、右和上边界,而没有涉及下边界。如果浮动元素比父元素高,浮动元素会延伸,并超出父元素的下边界;如果设置了浮动元素的外边距,也会使浮动元素超出父元素边界。解决方法可参见《关于 float 的一些问题》。

(4) 如果一个浮动元素与正常流中的内容发生重叠,参见以下规则:
① 行内框与一个浮动元素重叠时,其边框、背景和内容都在该浮动元素「之上」显示。
② 块框与一个浮动元素重叠时,其边框和背景都在该浮动元素「之下」显示,而内容在浮动元素「之上」显示。

(5) 为避免元素左右出现浮动元素,可以为元素设置 clear 属性来清除浮动。

clear 的作用是在元素左右生成清除区域,在元素上外边距之上增加额外间隔,不允许任何浮动元素进入这个范围内。

3. 定位

利用定位,可以准确地定义元素框相对于其正常位置应该出现在哪里,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置。

position 可以选择 4 种不同类型的值。
① static:元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分,行内元素则会创建一个或多个行框,置于父元素中。
② relative:元素框偏移某个距离。元素仍保留未定位前的形状,他原本所占的空间仍保留。
③ absolute:元素框从文本流完全删除,并相对于其包含块定位,包含块可以是文档中另一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像该元素原来不存在一样。
④ fixed:表现类似于 absolute,不过其包含块为视窗本身。

对于浮动元素,其包含块是最近的块级祖先元素。而对于定位元素,情况就会变得尤为复杂。
①「根元素」的包含块(也称之为初始包含块)由用户代理建立。在 HTML 中,根元素一般为 html。
② 对于一个非根元素,如果其 position 值是 relative 或 static,包含块则由最近的块级框、表格单元或行内块祖先框的内容边界构成。
③ 对于一个非根元素,如果其 position 值是 absolute,包含块设置为最近的 position 值不是 static 的祖先元素(可以是任何类型)。通常会选择一个元素作为绝对定位元素的包含块,将其 position 指定为 relative 并且不设置偏移。

定位机制使用了 4 个属性来描述定位元素各边相对于其包含块的偏移,我们称这 4 个属性为「偏移属性」。

偏移定位元素的外边距边界时,带来的影响是元素的所有一切(包括外边距、边框、内边距和内容)都会在定位过程中移动。

3.1 绝对定位

元素绝对定位时,会从文档流中完全删除。然后相对于其包含块定位,其边界根据偏移属性放置。定位元素不会流入其他内容,反之亦然。这说明,绝对定位元素可能会覆盖其他元素,或者被其他元素所覆盖。

元素绝对定位时,如果 bottom 外某个任意偏移属性设置为 auto,会有一种特殊的行为。对于 left 和 right,元素的左右边界会相对于其包含块的左右边界放置(包含块可以是初始包含块);而对于 top,定位的元素会根据其静态位置(元素在正常流中原本的位置)进行对齐。

3.1.1 非替换元素的绝对定位

对于非替换元素,如果一个绝对定位元素的大小在水平方向上过度受限,将忽略 right 的值;如果一个绝对定位元素的大小在垂直方向上过度受限,将忽略 bottom 的值。因此,绝对定位的非替换元素的 width 会「恰当收放」。

3.1.2 非替换元素的绝对定位

由于非替换元素有固有的宽度和高度因此大小不会改变,只能进行显式地修改,所以绝对定位的替换元素没有「恰当收放」的概念。

沿水平轴的布局受以下规则控制:
1- 如果 width 设置为 auto,width 的实际使用值由元素内容的固有宽度决定。
2- 从左向右读的言语中,如果 left 值为 auto,要把 auto 替换为静态位置。
3- 如果 left 或 right 仍为 auto(即未在上一步被替换),则将 margin-left 或 margin-right 的值替换为 0。
4- 如果此时 margin-left 和 margin-right 都还定义为 auto,则把它们设置为相同的值,从而将元素所在的包含块居中。
5- 在此之后,如果只剩下一个 auto 值,将其修改为余下剩余的部分。

沿垂直轴的布局受以下规则控制:
1- 如果 height 设置为 auto,height 的实际使用值由元素内容的固有高度决定。
2- 如果 top 值为 auto,要把 auto 替换为静态位置。
3- 如果 bottom 为 auto,则将 margin-top 或 margin-bottom 的值替换为 0。
4- 如果此时 margin-top 和 margin-bottom 都还定义为 auto,则把它们设置为相同的值,从而将元素所在的包含块居中。
5- 在此之后,如果只剩下一个 auto 值,将其修改为余下剩余的部分。

与替换元素一样,如果值在水平(垂直)方向上过度受限,将忽略 bottom(bottom) 的值。

3.2 固定定位

固定定位与绝对定位很相似,只不过固定定位的包含块是视窗。固定定位时,元素会从完全从文档流中去除,不会有相对于文档中任何部分的位置。

3.3 相对定位

相对定位是一个理解起来比较简单的定位方式。当元素相对定位时,它会从正常的位置移走,不过,原来所占的空间并不会因此消失。而且,可以移动一个相对定位元素来遮挡其他内容。

有意思的是,当相对定位出现过度受限的情况时,一个值会重置成另一个数的相反数。因此,bottom 总是等于 -top,right 总是等于 -left(从左到右读的语言中)。

3.4 z-index

对于所有的定位,最后都不免会出现这样一种情况:两个元素试图放在同一位置上。但是这样就会出现覆盖的现象 —— 所以,为了解决控制哪一个元素在上层的问题,特意引入了 z-index 属性。

利用 z-index 属性,可以改变元素相互覆盖的顺序。拥有较高 z-index 值的元素比 z-index 值较低的元素离读者更近。z-index 值可以不连续。

由于 CSS 规定:当前叠放上下文中生成框的栈层次与父框的层次相同。这个框不会建立新的局部层叠上下文。因此,如果元素设置为 z-index: auto;,可以将其处理为 z-index: 0;

4. 多列布局

如果一行太长,人们阅读文本很麻烦; 如果眼睛从一行的终点移动到下一个行的开始需要太长时间,它们就会丢失它们所在的行。因此,为了最大限度地利用大屏幕,可以将宽度不等的文本列并排放置,就像报纸一样。CSS 多列布局的出现很好地解决了这个问题,能让我们更容易地定义多列文本。

4.1 列计数器和宽度

4.1.1 列计数器

属性 column-count 设置特定数量的列数。

column-count 属性有 2 种值可选:
① auto:默认值,由其他例如 column-width 的属性值决定
② (number): 严格的正数,用来描述元素内容被划分的理想列数。假如 column-width 也被设置为非零值,此参数仅表示所允许的最大列数。

4.1.2 宽度

属性 column-width 设置期望的最小列宽。

column-width 属性有 2 种值可选:
① auto:默认值,由其他例如 column-count 的属性值决定。
② (length): 严格的正数,用来描述元素内容被划分后每列的的宽度。

4.1.3 简写

columns 是一个简写的 CSS 属性,允许同时设置 column-widthcolumn-count 属性。

4.2 列间隙

列与列之间有缝隙,该值可通过设置 column-gap 属性来修改。

column-gap 属性有 3 种值可选:
① normal:默认值,规定列间间隔为一个常规的间隔。W3C 建议的值是 1em。
② (length): 把列间的间隔设置为指定的长度。length 值必须为非负数。
③ (percentage): 把列间的间隔设置为指定的百分比。percentage 值必须为非负数。

4.3 其他属性

4.3.1 分列样式

column-rule-width 让你可以设置在多列布局中被画在两列之间的规则(线条)的宽度。

column-rule-style 让你可以设置在多列布局中被画在两列之间的规则(线条)的样式。

column-rule-color 让你可以设置在多列布局中被画在两列之间的规则(线条)的颜色。

同边框属性一样,可以使用简写属性 column-rule 设置列与列之间的边框宽度、样式和颜色。

4.3.2 跨越列

可以通过设置 column-span 使元素横跨所有列。

column-span 属性有 2 种值可选:
① none:元素不跨多个列。
② all:元素横跨所有列。元素出现之前,出现在元素之前的正常流中的内容在所有列之间自动平衡。该元素建立一个新的块格式上下文。

4.3.3 填充列

column-fill 可以设置元素所有列的高度是否统一。

column-fill 属性有 2 种值可选:
① auto:列高度自适应内容。
② balance:所有列的高度以其中最高的一列统一。

4.4 优雅降级

多列属性会被不支持多列模型的浏览器忽略。所以并不是所有的浏览器都支持不带前缀的属性名。为了在大多数现代浏览器中应用这种特性,每个属性必须写三次: 一次用 -moz 前缀,一次用 -webkit 前缀,一次不使用前缀。

5. Flex 布局

要开始使用 Flexbox,必须先让父元素变成一个 Flex 容器。你可以在父元素中显式的设置 display:flex 或者 display:inline-flex。就这么的简单,这样你就可以开始使用 Flexbox 模块。

5.1 基本概念

在学习 Flex 布局之前,需要对一些涉及弹性布局的术语进行了解。

① flex container / flex items:Flex 容器 —— 父元素显式设置了 display:flex / Flex 项目 —— Flex容器内的子元素
② Main Axis:进行布局时作为布局基准的轴,称为主轴。在横向布局时为水平轴,在纵向布局时为垂直轴。
③ main-start / main-end:进行布局时的布局起点和布局终点。在横向布局时为容器的左端和右端,在纵向布局时为容器的顶端和低端。
④ Cross Axis:与主轴垂直相交的轴,称为交叉轴。在横向布局时为垂直轴,在纵向布局时为水平轴。
⑤ cross-start / cross-end:Cross Axis 轴的起点和终点。在横向布局时为容器的顶端和低端,在纵向布局时为容器的左端和右端。
⑥ main size / cross size:单个项目占据的主轴空间叫做 main size,占据的交叉轴空间叫做 cross size。

5.2 Flex 容器属性

5.2.1 flex-direction

flex-direction 属性控制 Flex 项目沿着主轴(Main Axis)的排列方向。

flex-direction 属性有 4 种值可选:
① row:让 Flex 项目沿着 Main Axis 从左向右水平排列。
② row-reverse:项目沿着 Main Axis 从右向左水平排列。
③ column:Flex 项目将沿着 Cross Axis 从上到下垂直排列。
④ column-reverse:项目将沿着 Cross Axis 从下到上垂直排列。

5.2.2 flex-wrap

flex-wrap 指定 flex 元素单行显示还是多行显示。如果允许换行,这个属性允许你控制行的堆叠方向。

flex-wrap 属性有 3 种值可选:
① nowrap:flex 的元素被摆放到到一行,这可能导致溢出 flex 容器。
② wrap:flex 元素 被打断到多个行中。
③ wrap-reverse:和 wrap 的行为一样,但是 cross-start 和 cross-end 互换。

5.2.3 flex-flow

flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写形式。

5.2.4 justify-content

justify-content 属性主要用来控制 Flex 项目 在 Main-Axis 对齐方式。

justify-content 属性有 5 种值可选:
① flex-start:让所有 Flex 项目靠 Main-Axis 开始边缘左对齐。
② flex-end:让所有 Flex 项目靠 Main-Axis 结束边缘右对齐。
③ center:让所有 Flex 项目排在 Main-Axis 中间居中对齐。
④ space-between:让所有 Flex 项目两端对齐。
⑤ space-around:让每个 Flex 项目具有相同的空间,每个项目两侧的间隔相等。

5.2.5 align-items

align-items 属性主要用来控制 Flex 项目 在 Cross-Asis 对齐方式。

align-items 属性有 5 种值可选:
① flex-start:让所有 Flex 项目靠 Cross-Asis 开始边缘顶部对齐。
② flex-end:让所有 Flex 项目靠 Cross-Asis 结束边缘底部对齐。
③ center:让 Flex 项目在 Cross-Asis 中间居中对齐。
④ baseline:让所有 Flex 项目在 Cross-Asis 上沿着他们自己的基线对齐。
⑤ stretch:让所有的 Flex 项目高度和 Flex 容器高度一样。

5.2.6 align-content

align-content 属性用于多行的 Flex 容器。它也是用来控制 Flex 项目在 Flex 容器里的排列方式。

align-content 属性有 6 种值可选:
① flex-start:让多行 Flex 项目靠 Cross-Asis 开始边缘,让 Flex 项目沿着 Cross-Asis 从上到下排列。
② flex-end:让多行 Flex 项目靠着 Cross-Asis 结束位置,让 Flex 项目沿着 Cross-Asis 从下到上排列。
③ center:让多行 Flex 项目在 Cross-Asis 中间,在 Flex 容器中居中对齐。
④ space-between:让多行 Flex 项目在 Flex 容器中沿 Cross-Asis 两端对齐。
⑤ space-around:让多行 Flex 项目具有相同的空间,每行项目两侧的间隔相等。
⑥ stretch:会拉伸 Flex 项目,让他们沿着 Cross-Asis 适应 Flex 容器可用的空间。

5.3 Flex 项目属性

5.3.1 order

order 属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

5.3.2 flex-grow

flex-grow 属性定义弹性盒子项目的拉伸因子。

如果 Flex 项目的 width 样式属性值的总和小于 Flex 容器的宽度值,可以使用 flex-grow 属性来进行调整。
计算过程如下:
① 计 Flex 项目的 width 样式属性值的总和为 a,Flex 容器的宽度值的大小为 b,两者差值为 c = a - b。
② 计所有 Flex 项目 flex-grow 属性值的总和为 i,计算出每一份 flex-grow 属性值所占的大小,记为 x = c / i。
③ 调整后的 Flex 项目的 width 样式属性值 = 调整前的 Flex 项目的 width 样式属性值 + x * 该 Flex 项目的 flex-grow 属性值

5.3.3 flex-shrink

flex-shrink 属性定义弹性盒子项目的收缩因子。

如果 Flex 项目的 width 样式属性值的总和大于 Flex 容器的宽度值,可以使用 flex-shrink 属性来进行调整。
计算过程如下:
① 计 Flex 项目的 width 样式属性值的总和为 a,Flex 容器的宽度值的大小为 b,两者差值为 c = b - a。
② 计所有 Flex 项目 flex-shrink 属性值的总和为 i,计算出每一份 flex-shrink 属性值所占的大小,记为 x = c / i。
③ 调整后的 Flex 项目的 width 样式属性值 = 调整前的 Flex 项目的 width 样式属性值 - x * 该 Flex 项目的 flex-shrink 属性值

5.3.4 flex-basis

flex-basis 指定了 flex 元素在主轴方向上的初始大小。

注意:如果 flex-basis 属性的值是 0 时,也需要使用单位;当 flex-basis 属性值为 auto 时,Flex 项目的初始宽度计算是基于内容的大小。

5.3.5 flex

flexflex-growflex-shrinkflex-basis 三个属性的简写。

该属性有两个快捷值:auto (1 1 auto) —— 自动计算初始化宽度,但是如果有必要,会伸展或者收缩以适应整个可用宽度。 和 none (0 0 auto) —— 宽度是被自动计算,不过 Flex 项目不会伸展或者收缩。

5.3.6 align-self

align-self 会对齐当前 flex 行中的 flex 元素,并覆盖 align-items 的值。

align-self 属性有 6 种值可选:
① auto:表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。
② flex-start:flex 元素会对齐到 Cross-Asis 的首端。
③ flex-end:flex 元素会对齐到 Cross-Asis 的末端。
④ center:flex 元素会对齐到 Cross-Asis 的中间,如果该元素的 cross-size 的尺寸大于 flex 容器,将在两个方向均等溢出。
⑤ baseline:所有的 flex 元素会沿着基线对齐。
⑥ stretch:flex 元素将会基于容器的宽和高,按照自身 margin box 的 cross-size 拉伸。

6. 参考文档

[1] CSS 权威指南
[2] MDN 文档:CSS 布局
[3] MDN 文档:使用CSS的多列布局
[4] Flex 布局教程:语法篇
[5] 理解 Flexbox:你需要知道的一切(基础篇)
[6] 理解 Flexbox:你需要知道的一切(项目篇)