1. 引言
(本节为非规范性内容。)
本模块描述了 CSS 中的多栏布局。 使用本文档描述的功能, 样式表可以声明将某个元素的内容以多栏方式排布。
CSS 中的其他布局方式, 当应用于父元素时, 会改变其直接子元素的显示属性。 例如,若创建了三栏网格布局, 网格容器的直接子元素会变为网格项(grid items),并被放入列轨道中, 每个单元格一个元素,必要时可增加新行。
而多栏容器的子元素依然处于正常流中, 只不过该流被安排到若干栏中。 这些栏的内联尺寸是灵活的, 会根据可用空间变化而调整显示的栏数或栏的宽度。
多栏布局在 CSS 中很容易描述。 下面是一个简单的例子:
也可以在样式表中显式设置栏数:
可以使用简写 columns 属性在一条声明中设置其中一个或两个属性。
本模块还引入了一组用于描述栏间间隙和分隔线的属性。
body{ column-gap : 1 em ; column-rule : thin solid black; }
上例的第一条声明设置了两栏之间的间隙为 1em。 栏间隙类似于内边距区域。 在间隙中间会有一条分隔线, 其样式由 column-rule 属性描述。
column-rule 属性的取值类似于 CSS border 属性。 就像 border,column-rule 也是一个简写属性。
body{ column-gap : 1 em ; column-rule-width : thin; column-rule-style : solid; column-rule-color : black; }
column-fill 和 column-span 属性 让样式表在多栏布局中拥有更丰富的视觉表现力。
本规范引入了十个新属性, 上述示例全部用到了这些属性。
如果所有栏属性都为初始值, 元素布局将与仅有一栏的多栏布局完全一致。
1.1. 值定义
本规范遵循 CSS 属性定义约定 [CSS21],并采用 值定义语法 [CSS-VALUES-3]。 本规范未定义的值类型,在 CSS Values & Units [CSS-VALUES-3] 中定义。 与其他 CSS 模块组合时,这些值类型定义可能会扩展。
除了在各自定义中列出的属性专用值外, 本规范定义的所有属性还接受 CSS 通用关键字 作为属性值。 为了可读性,这些值未在各属性定义中重复列出。
2. 模块交互
本文档定义了早期规范中未出现的新特性。 最终版本发布后,将替代以下内容:
3. 多栏模型
当元素的 column-width 或 column-count 属性不是 auto 时,该元素会建立一个多栏容器(简称 multicol container), 进而作为多栏布局的容器。
在传统 CSS 盒模型中, 元素内容流入对应元素的内容盒中。 多栏布局引入了一种由分片上下文组成的模型,该上下文由匿名分片容器构成,称为栏盒(简称 columns)。 这些栏盒建立独立的块格式化上下文,多栏容器的内容流入其中, 并作为其非定位子项的包含块。
img{ display : block; width : 100 % ; }
由于栏盒会创建新的块格式化上下文, width 会相对于栏盒计算, 因此图片不会溢出栏盒:
出现在多栏布局内的浮动元素,其定位以其所在栏盒为准。
img{ display : block; float : right; }
在 HTML 中,图片出现在“the leg of a chicken”句子之后。
注意: 栏盒是匿名盒, 不会成为包含块,不作为绝对定位盒的包含块。 position 属性建立这些盒的包含块, 作用于多栏容器(即主盒)。
.container{ position : relative; column-count : 3 ; } img{ position : absolute; top : 20 px ; left : 40 px ; }
多栏容器的脱离流后代会影响栏的平衡以及 多栏容器 的块尺寸。
栏盒的排列顺序依赖于多栏容器的内联基方向,并被组织为多栏行。 栏宽指栏盒在内联方向的长度。 栏高指栏盒在块方向的长度。 同一行内所有栏盒宽度一致,高度一致。
注意: 在使用垂直书写模式的文本中, 块方向是水平方向。 垂直书写模式下,栏水平排列, 块流方向可为从右到左或从左到右。 column-width 属性因此指栏的内联尺寸, 而不是物理上的水平宽度。
在多栏容器的每个多栏行内, 相邻栏盒之间用一个栏间隙分隔, 其中可包含一个栏分隔线。 同一多栏容器内所有栏隙相等, 所有栏分隔线(如有)也相等; 栏分隔线只出现在两侧均有内容的栏之间。
最简单情况下,多栏容器仅含一行栏,且每栏高度等于多栏容器内容盒的实际高度。 但分片或跨栏元素可将多栏容器内容分为多行多栏行。
若多栏容器被分页,每栏的高度受页面约束,内容会在下一页新行栏盒中继续;栏盒不会跨页分割。
当 跨栏元素 分割多栏容器时也会出现类似效果:跨栏元素前的各栏会被平衡并缩短以适应内容,跨栏元素后的内容流入一组新的栏盒。
因此,多栏容器是一个常规的块容器,会建立新的独立格式化上下文,其内容由一系列多栏行和跨栏元素组成。 每个 多栏行 作为一个块级盒,为其栏盒建立多栏格式化上下文; 每个跨栏元素作为 块级盒,根据其 display 属性建立相应类型的独立格式化上下文。
允许嵌套多栏容器, 但实现上可能有特定限制。
注意: 不能对栏盒设置属性/取值。 例如无法为某个特定栏盒设置背景,栏盒也没有内边距、外边距或边框的概念。 未来的规范可能会增加更多功能。 例如支持不同宽度、不同背景的栏。
注意: 栏高大于视口的多栏容器可能带来可访问性问题。 详见可访问性注意事项。
4. 栏数与栏宽
确定栏数和栏宽是进行多栏内容布局的基础。 以下属性用于设置栏的数量和宽度:
第三个属性 columns, 是一个简写属性,用于同时设置 column-width 和 column-count。
其他因素,如显式的栏断点、内容和高度约束,也可能影响实际的栏数和栏宽。
4.1. 栏的内联尺寸:column-width 属性
| 名称: | column-width |
|---|---|
| 取值: | auto | <length [0,∞]> |
| 初始值: | auto |
| 适用于: | 块容器,不包括 表包装盒 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 关键字 auto 或绝对长度 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值类型 |
此属性描述多栏容器中栏的宽度。
- auto
- 表示栏宽由其他属性决定 (如 column-count 不是 auto 时)。
- <length [0,∞]>
- 描述理想的栏宽。 实际栏宽可能会变宽(以填满可用空间), 也可能变窄(仅当可用空间小于设定栏宽时)。 不允许负值。 使用值会被限制为最小 1px。
div{ width : 100 px ; column-width : 45 px ; column-gap : 0 ; column-rule : none; }
在 100px 宽的元素内,可以容纳两个 45px 宽的栏。 为了填满可用空间, 实际栏宽会被增加为 50px。
div{ width : 40 px ; column-width : 45 px ; column-gap : 0 ; column-rule : none; }
可用空间小于指定的栏宽, 因此实际栏宽会被缩小。
为确保 column-width 可用于竖排文本, 栏宽指栏内部行盒的长度。
注意: 设置 column-width 具有一定灵活性的原因是为了实现可适配多种屏幕的可扩展设计。 若要设置精确栏宽, 还必须同时指定栏间隙和多栏容器宽度(假设为横排文本)。
4.2. 栏数:column-count 属性
| 名称: | column-count |
|---|---|
| 取值: | auto | <integer [1,∞]> |
| 初始值: | auto |
| 适用于: | 块容器,不包括 表包装盒 |
| 是否继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 指定值 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值 |
此属性描述多栏容器的栏数。
- auto
- 表示栏数由其他属性决定 (如 column-width 不是 auto 时)。
- <integer [1,∞]>
- 描述内容将被流入的理想栏数。 取值必须大于 0。 若 column-width 和 column-count 都不是 auto, 则整数值表示最大栏数。
4.3. column-width 和 column-count 简写:columns 属性
| 名称: | columns |
|---|---|
| 取值: | <'column-width'> || <'column-count'> |
| 初始值: | 见各自属性 |
| 适用于: | 见各自属性 |
| 是否继承: | 见各自属性 |
| 百分比: | 见各自属性 |
| 计算值: | 见各自属性 |
| 动画类型: | 见各自属性 |
| 规范顺序: | 按语法 |
这是用于同时设置 column-width 和 column-count 的简写属性。 省略的值会被设置为其初始值。
columns : 12 em ; /* column-width: 12em; column-count: auto */ columns: auto12 em ; /* column-width: 12em; column-count: auto */ columns:2 ; /* column-width: auto; column-count: 2 */ columns:2 auto; /* column-width: auto; column-count: 2 */ columns: auto; /* column-width: auto; column-count: auto */ columns: auto auto; /* column-width: auto; column-count: auto */
4.4. 伪算法
下面的伪算法用于计算 column-count(N)和 column-width(W)的使用值。伪算法中还有一个变量:U 表示多栏容器的使用宽度。
注意: 多栏容器的使用宽度 U 可能依赖于元素内容, 此时也取决于 column-count 和 column-width 的计算值。 本规范未定义 U 的计算方式, 另一份规范(可能为基本盒模型 [CSS3BOX] 或 盒尺寸模块 [CSS3-SIZING])将定义此内容。
floor(X) 函数返回不大于 X 的最大整数 Y。
(01) if ((column-width = auto) and (column-count = auto)) then (02) exit; /* not a multicol container */ (03) if column-width = auto then (04) N := column-count (05) else if column-count = auto then (06) N := max(1, (07) floor((U + column-gap)/(column-width + column-gap))) (08) else (09) N := min(column-count, max(1, (10) floor((U + column-gap)/(column-width + column-gap))))
并且:
(11) W := max(0, ((U + column-gap)/N - column-gap))
为确定自动重复栏数, UA 必须将栏尺寸向下取整到 UA 指定的值以避免除零错误。 建议此取整为 1px 或更小。
在分片上下文中,如 分页媒体, 用户代理可针对每个分片分别执行该计算。
column-count 的使用值计算时不考虑显式栏断点或栏高约束, 而实际值会考虑这些因素。
div{ width : 40 em ; columns : 20 em ; column-gap : 0 ; } p{ break-after : column; }
< div > < p > one< p > two< p > three</ div >
div{ width : 80 em ; height : 10 em ; columns : 20 em ; column-gap : 0 ; column-fill : auto; }
< div > foo</ div >
计算栏数为 auto, 使用栏数为 4, 实际栏数为 1。
4.5. 堆叠上下文
多列容器中的所有列框都处于同一个堆叠上下文中,其内容的绘制顺序如 CSS 2.1 所规定。列框不会建立新的堆叠上下文。
4.6. 列样式:::column 伪元素
::column 伪元素 表示多列容器中的单独列。 它只存在于多列容器上。
一个多列容器拥有与其列数相同数量的::column伪元素。 它们不能被单独选中; 任何应用于多列容器 ::column 伪元素的样式 都会应用到该元素上的所有列伪元素上。 每个 ::column 的尺寸和位置 与其对应的列一致: 在行内轴上与该列可用空间一致, 在块轴上与容器的内容框一致。 (它不会覆盖列间的间隙/分隔线。)
::column 伪元素被视为其多列容器的子元素, 但没有任何内容。 它们不会包裹列的内容, 只是填充相同的空间。
::column 伪元素只接受非常有限的属性:
-
scroll-margin、scroll-snap-align 和 scroll-snap-stop(这些滚动捕捉属性应用于滚动容器内部的元素)
此外,::column 伪元素可以拥有自己的 ::scroll-marker 伪元素, 形式为 ::column::scroll-marker。 (其他伪元素不会出现在::column上。) 这样的 ::scroll-marker 伪元素 继承自 ::column 伪元素的起始元素, 而不是 ::column 本身。
注: 适用于 ::column 的属性和伪元素列表 未来可能会扩展, 随着我们开发更多能够关心列位置而不关心内容的新特性。
5. 列间隙与分隔线
列间隙和分隔线被放置在同一个多列容器的各列之间。 列间隙和分隔线的长度等于列的高度。 列间隙会占用空间。 也就是说,列间隙会将相邻列的内容分开 (在同一个多列容器内)。
列分隔线绘制在列间隙的中间, 两端在多列容器的相对内容边缘。 列分隔线不占用空间。 也就是说,列分隔线的存在或宽度不会影响其他内容的布局。 如果 列分隔线 宽度大于间隙, 相邻的列框会覆盖分隔线, 并且分隔线可能会超出多列容器的盒子。 列分隔线绘制在多列容器的边框之上。 对于可滚动的多列容器, 注意虽然多列容器的边框和背景不会跟随滚动, 但分隔线需要与列一起滚动。 列分隔线只会绘制在两个都有内容的列之间。
5.1. 列间隙:column-gap 属性
column-gap 属性在 [CSS3-ALIGN] 中定义。
在多列排版上下文中,normal 用作 column-gap 属性的有效值为 1em。 这样可以确保使用初始值时各列内容可读。 如果列之间存在分隔线, 它会出现在间隙的中间。
5.2. 列分隔线颜色:column-rule-color 属性
| 名称: | column-rule-color |
|---|---|
| 值: | <color> |
| 初始值: | currentcolor |
| 适用对象: | 多列容器 |
| 是否继承: | 否 |
| 百分比: | N/A |
| 计算值: | 计算后的颜色 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值类型 |
- <color>
- 指定列分隔线的颜色。
5.3. 列分隔线样式:column-rule-style 属性
| 名称: | column-rule-style |
|---|---|
| 值: | <line-style> |
| 初始值: | none |
| 适用对象: | 多列容器 |
| 是否继承: | 否 |
| 百分比: | N/A |
| 计算值: | 指定的关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
column-rule-style 属性设置元素各列之间分隔线的样式。 <line-style> 的取值方式与边框折叠模型一致。
none 和 值会强制 column-rule-width 的计算值为 0。
5.4. 列分隔线宽度:column-rule-width 属性
| 名称: | column-rule-width |
|---|---|
| 值: | <line-width> |
| 初始值: | medium |
| 适用对象: | 多列容器 |
| 是否继承: | 否 |
| 百分比: | N/A |
| 计算值: | 绝对长度, 按边框宽度对齐;如果列分隔线样式为 none 或 时为 0 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值类型 |
该属性设置列间分隔线的宽度。 不允许为负值。
5.5. 列分隔线速记属性:column-rule 属性
| 名称: | column-rule |
|---|---|
| 值: | <'column-rule-width'> || <'column-rule-style'> || <'column-rule-color'> |
| 初始值: | 见各独立属性 |
| 适用对象: | 见各独立属性 |
| 是否继承: | 见各独立属性 |
| 百分比: | 见各独立属性 |
| 计算值: | 见各独立属性 |
| 动画类型: | 见各独立属性 |
| 规范顺序: | 按语法 |
该属性用于在样式表中同时设置 column-rule-width、column-rule-style 和 column-rule-color。 省略的值将被设为各自的初始值。
body{ column-gap : 35 px ; column-rule-width : 35 px ; column-rule-style : solid; column-rule-color : black; }
6. 列断开
当内容以多列布局时, 用户代理必须决定列断点的位置。 将内容分割为多列的问题与分页类似, 详见 CSS 2.1 第 13.3.3 节 [CSS21]。
引入了三个新属性,以便可在与分页断点相同的属性中描述列断点:break-before、break-after 和 break-inside。
6.1. 控制分片:break-before、break-after、break-inside 属性
break-before、break-after 和 break-inside 在 [CSS3-BREAK]中定义。
7. 跨列
column-span 属性可以让一个元素跨越多列显示。 本规范为上一等级的可选值中增加了 <integer>。
7.1. column-span
| 名称: | column-span |
|---|---|
| 值: | none | <integer [1,∞]> | all | auto |
| 初始值: | none |
| 适用对象: | 流内的块级元素 |
| 是否继承: | 否 |
| 百分比: | N/A |
| 计算值: | 指定值 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
该属性描述元素跨越多少列。可选值有:
- none
- 元素不会跨越多列。
- all
-
元素会强制发生列断点,并被 脱离文档流,
跨越其最近的多列祖先
(同一块格式化上下文内)的所有列。
正常流中出现在该元素之前的内容
会在该元素出现之前自动在所有列中均衡分布,
后续内容会在该元素之后流入新的 多列行。
该元素会建立一个独立格式化上下文。
注: 元素是否建立新的格式化上下文,与它是否为多列的后代无关。 当 column-span 设置为 all 时,总是如此。 这样有助于在后续移除多列或媒体查询关闭多列时保持设计的健壮性。
- <integer [1,∞]>
-
元素跨越指定数量的列。
值必须大于 0。
如果指定的整数等于或大于多列元素的列数,
跨越的列数效果等同于指定 column-span: all。
此定义尚不充分。
- column-span: 1 应等同 column-span: none, 还是应该创建一个跨列元素(spanner,BFC)?
- 到底跨了哪几列?
- 这对高度计算和 column-fill 有什么影响?
- auto
-
元素跨越多少列依赖于其最小内容和外部尺寸
在多列容器的行内方向上的值。
如果比 column-width 的用值还小,
则等同于 column-span: none。
否则,跨越的列数为最小正整数 n,满足
n × column-width + (n - 1) × column-gap大于其 最小内容 外部尺寸。 如果大于容器列数, 则效果等同于 column-span: all。若 column-span: 1 不等同于 column-span: none, 那么当元素本身很小时应表现为 column-span: 1 还是 column-span: none?
跨越多列的元素被称为 多列跨越元素, 其创建的盒子称为 跨列盒(spanner)。
跨列盒 的 包含块是 多列容器 本身。 因此,如果跨列盒本身未建立 包含块用于其内部的绝对定位盒, 那么它们的 包含块链会直接跳到 多列容器(跳过 跨列盒到 多列容器之间的任何祖先)。
尽管跨列盒被脱离文档流, 这不会影响跨列元素的绘制顺序 [CSS21]。
h2 元素被添加到示例文档的第六句话之后
(即“the leg of a”之后)。
应用的样式如下:
h2{ column-span : all; background : silver}
将 column-span 设为 all 后,
h2 元素之前的所有内容
都会显示在 h2 元素之上。
只要属于同一个格式化上下文, 并且跨列元素和多列容器之间没有建立固定定位后代的包含块的东西, 跨列元素可以低于第一层后代出现。
< article > < section > < div class = "spanner" > Attempted spanner</ div > </ section > </ article >
article{ columns : 2 ; } section{ transform : rotate ( 90 deg ); } .spanner{ column-span : all; background : silver; }
如果跨列盒前的片段为空,则不会有特殊情况; 顶部的 margin/border/padding 会出现在跨列元素上方,作为一个空的片段。
article 元素。
这个父元素内有一个段落和一个 section 元素。
section 内有一个 h2 标题设置为 all,它跨越了所有三列,
而 section 本身仍在列内。
h2 是 section 的第一个子元素。
这意味着 section 的 margin、border(红色显示)和 padding 会作为空片段出现在跨列 h2 之前。
< article > < p > ...</ p > < section > < h2 > An h2 element</ h2 > < p > ...</ p > </ section > </ article >
section{ border : 2 px solid red; margin-top : 65 px ; padding-top : 20 px ; } h2{ column-span : all; background : silver}
h2 元素设置为 column-span: all,
section 有红色边框和顶部内外边距跨列元素占用空间比普通元素更多。 当空间有限时,可能无法为其留出足够空间。 此时,用户代理可以将该元素视为本属性值为 none。
h2 元素出现在内容较后的位置,
多列容器的高度受限。
因此,h2 出现在溢出部分,无法跨列显示。
结果,该元素表现得就像指定了 column-span: none。
跨列盒为块级盒, 因此两个相邻跨列盒的外边距会合并。 仅被绝对定位项分隔的两个跨列盒的外边距也会合并, 因为绝对定位项不会创建列框。 由于列框会建立新的块格式化上下文, 列框内部元素的外边距不会和跨列盒的外边距合并。
h2{ margin : 16 px 0 ; column-span : all; background : silver} p{ margin-top : 16 px }
8. 填充列
填充列有两种策略: 可以让各列均衡,或者不均衡。 如果各列均衡,用户代理应尽量使列高变化最小, 同时要遵循强制断点、widows 和 orphans 及其它可能影响列高的属性。 如果不均衡,则按顺序填充各列; 有些列可能只填了一部分,甚至完全为空。
8.1. 列均衡:column-fill 属性
| 名称: | column-fill |
|---|---|
| 值: | auto | balance | balance-all |
| 初始值: | balance |
| 适用对象: | 多列容器 |
| 是否继承: | 否 |
| 百分比: | N/A |
| 计算值: | 指定关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
该属性指定 在多列行中 不是紧接着一个跨列盒的内容是否在各列间均衡分布。
可选值为:
- balance
- 内容尽量在各列均衡分布。 在分片环境下,仅最后一个分片均衡。
- balance-all
- 内容尽量在各列均衡分布。 在分片环境下,所有分片均均衡。
- auto
- 顺序填充各列
在连续环境中,当存在溢出列时该属性无效。
article{ width : 60 em ; height : auto; columns : 4 ; column-fill : balance; }
article{ width : 60 em ; height : 4 em ; columns : 4 ; column-fill : auto; }
结果,所有内容都填充在第一列:
article{ width : 60 em ; height : auto; columns : 4 ; column-fill : balance; } p{ break-after : column; }
最短列高为五行文本。 列高确定后,内容顺序填充各列。 结果第三列与前两列等高,最后一列远远较短。
article{ width : 60 em ; height : auto; columns : 4 ; column-fill : balance; }
本例,article 以一个不可断开的 figure 开头,这决定了列高。 随后内容被顺序填充到剩余列:
9. 溢出
9.1. 多列容器内部的溢出
除非会导致列断开,否则超出列框的内容会直接溢出显示,并不会被列框裁剪。
注: 关于列断开见§ 6 列断开, 关于是否被裁剪到多列容器的内容框见§ 9.2 多列容器外部的分页与溢出。
9.2. 多列容器外部的分页与溢出
超出多列容器边缘的内容和列分隔线会根据overflow属性被裁剪。
由于以下原因,多列容器可能会拥有超出空间的列数:
- 声明了限制列高的属性 (如使用height 或 max-height)。 此时,额外的列框会在行内方向生成
- 页面的尺寸。 此时,额外的列框会移到下一页。
- 显式的列断开。 此时,连续环境下额外列框会在行内方向生成,分片媒体下会移到下一个分片。
在连续环境中,出现在多列容器外的列被称为溢出列。 溢出列可能会影响多列容器的高度。
div{ max-height : 5 em ; overflow : visible; }
因此,列数会增加。
第二页效果如下:
由于列均衡,最后一个段落会分布在三列中。
附录 B. 变更记录
本附录为说明性内容。
与 2024年5月16日的 CSS-MULTICOL-1 候选推荐(CR)的变更
- 新增::column伪元素。2024年9月27日决议。
- 为<integer>和auto值的 column-span 增加了草案。
隐私注意事项
多列布局不会引入新的隐私泄露。
安全注意事项
多列布局不会引入新的安全问题。
可访问性注意事项
设置容器高度和行长可能会给视觉或认知障碍人士带来挑战。 参见 WCAG 成功标准 1.4.10 重排 和 WCAG 1.4.8 可视化呈现 以了解用户需求。
致谢
本文档基于 Håkon Wium Lie 在 [CSS3-MULTICOL] 中的工作, 并参考了多个早期方案及评论。 贡献者包括:
Alex Mogilevsky、 Andy Clarke、 Anton Prowse、 Bert Bos、 Björn Höhrmann、 Cédric Savarese、 Chris Lilley、 Chris Wilson、 Daniel Glazman、 Dave Raggett、 David Hyatt、 David Singer、 David Woolley、 Elika Etemad、 Giovanni Campagna、 Ian Hickson、 Joost de Valk、 Kevin Lawver、 L. David Baron、 Markus Mielke、 Melinda Grant、 Michael Day、 Morten Stenshorne、 Øyvind Stenhaug、 Peter Linss、 Peter-Paul Koch、 Robert O’Callahan、 Robert Stevahn、 Sergey Genkin、 Shelby Moore、 Steve Zilles、 Sylvain Galineau、 Tantek Çelik、 Till Halbach