1. 引言
本节为非规范性内容
许多类型的信息(例如:过去一年收集的天气读数) 最适合以二维网格进行可视化表示, 其中行表示列表中的一个项目 (例如:某个日期,以及当天测量的各种天气属性), 而列表示某个项目属性的连续值 (例如:过去一年测量的温度)。
有时,为了让表示更容易理解, 网格中的某些单元格会用来表示其父行/列的描述或摘要, 而不是实际数据。 这种情况更常发生在 第一行和/或第一列中的单元格(称为表头) 或最后一行和/或最后一列中的单元格(称为表尾)。
这种表格数据表示通常称为表格。 表格布局可能被滥用于渲染其他类似网格的表示,如日历或时间线, 但当所表示的信息不适合作为数据表时, 作者应优先使用其他布局模式。
HTML 中表格的渲染长期以来已经在 HTML 规范中定义。 然而,它与 CSS 中定义的特性的交互长期以来仍未定义。 本规范的目标是定义 同时支持 HTML 表格和 CSS 的用户代理的预期行为。
请注意,本文档中定义的某些行为 并不是解决其目标问题的最合乎逻辑或最有用的方式, 但这些行为通常是兼容性要求的结果,而不是本规范 编辑者的有意选择。 希望使用更复杂布局的作者 鼓励依赖更现代的 CSS 模块,例如 CSS Grid。
测试
以下测试是崩溃测试, 它们与本规范所描述特性的常规用法有关, 但不绑定到任何特定的规范性陈述。
- colspan-zero-crash.html (实时测试) (源代码)
- caption-repaint-crash.html (实时 测试) (源代码)
- caption-with-multicol-table-cell.html (实时 测试) (源代码)
- cell-contents-with-negative-outer-size.html (实时 测试) (源代码)
- col_span_dynamic_crash.html (实时 测试) (源代码)
- dialog-table-crash.html (实时 测试) (源代码)
- dynamic-recompute-baseline.html (实时 测试) (源代码)
- dynamic_col_width_crash.html (实时 测试) (源代码)
- dynamic_table_layout_crash.html (实时 测试) (源代码)
- empty-tbody-after-tall-section-crash.html (实时 测试) (源代码)
- empty_cells_crash.html (实时 测试) (源代码)
- empty_table_with_cols.html (实时 测试) (源代码)
- expression_width_crash.html (实时 测试) (源代码)
- inline-splitting-crash.html (实时 测试) (源代码)
- large-border-crash.html (实时 测试) (源代码)
- large-col-widths.html (实时 测试) (源代码)
- legacy_ng_mix_crash.html (实时 测试) (源代码)
- move-oof-inside-section-row-with-borders.html (实时 测试) (源代码)
- negative-section-distribution.html (实时 测试) (源代码)
- negative_caption_margin.html (实时 测试) (源代码)
- orthogonal-cell-borders.html (实时 测试) (源代码)
- orthogonal-cell-crash.html (实时 测试) (源代码)
- table-column-display-change-chrome-crash.html (实时 测试) (源代码)
- textarea-intrinsic-size-crash.html (实时 测试) (源代码)
- transition-table-row-group-crash.html (实时 测试) (源代码)
- uninitialized_read_crash.html (实时 测试) (源代码)
- vertical_percentage_crash.html (实时 测试) (源代码)
- tfoot-crash-print.html (实时测试) (源代码)
- th-text-align.html (实时测试) (源代码)
- toggle-row-display-property-001.html (实时 测试) (源代码)
- table-collapsed-row-or-column-crash.html (实时 测试) (源代码)
与表格布局大体相关的测试
- absolute-tables-001.html (实时测试) (源代码)
- absolute-tables-006.html (实时测试) (源代码)
- anonymous-table-cell-margin-collapsing.html (实时 测试) (源代码)
- chrome-rowspan-bug.html (实时测试) (源代码)
- col_removal.html (实时测试) (源代码)
- colspan-001.html (实时测试) (源代码)
- colspan-002.html (实时测试) (源代码)
- colspan-003.html (实时测试) (源代码)
- colspan-004.html (实时测试) (源代码)
- dynamic-rowspan-change.html (实时 测试) (源代码)
- html-display-table.html (实时测试) (源代码)
- inheritance.html (实时测试) (源代码)
- multicol-table-collapsed-border-crash.html (实时 测试) (源代码)
- multicol-table-crash.html (实时 测试) (源代码)
- remove-caption-from-anon-table.html (实时 测试) (源代码)
- remove-colgroup-from-anon-table.html (实时 测试) (源代码)
- row-group-order.html (实时测试) (源代码)
- table-position-sticky-computed.html (实时 测试) (源代码)
- table-cell-child-overflow-measure.html (实时 测试) (源代码)
- table-cell-inline-size-box-sizing-quirks.html (实时 测试) (源代码)
- table-cell-scroll-height.html (实时 测试) (源代码)
- table-cell-writing-mode-computed.html (实时 测试) (源代码)
- zero-rowspan-001.html (实时测试) (源代码)
- zero-rowspan-002.html (实时测试) (源代码)
- subpixel-table-cell-height-001.html (实时 测试) (源代码)
- subpixel-table-width-001.html (实时 测试) (源代码)
1.1. 值定义
本规范遵循 CSS 属性 定义约定,该约定来自 [CSS2],并使用来自 值定义语法的 [CSS-VALUES-3]。 本规范中未定义的值类型定义于 CSS Values & Units [CSS-VALUES-3]。 与其他 CSS 模块结合使用可能会扩展这些值类型的定义。
除各属性定义中列出的属性特有值外, 本规范中定义的所有属性 还都接受 CSS-wide 关键字作为其属性值。 为了可读性,这里没有显式重复列出它们。
2. 内容模型
2.1. 表格结构
CSS 表格模型基于 HTML4 表格模型, 在该模型中,表格的结构与表格的视觉布局紧密对应。 在此模型中,表格由一个可选标题和任意数量的单元格行组成。
此外,相邻的行和列可以按结构分组,并且 这种分组可以体现在呈现中(例如,可以围绕一组行绘制边框)。
表格模型被称为“行优先”,因为 作者在文档语言中显式指定的是行,而不是列。 列会在所有行都指定后派生出来: 第一行的第一个单元格属于第一列 以及跨越所需的任意其他列(必要时会创建它们), 而该行后续的每个单元格都属于下一个可用列 以及跨越所需的任意其他列(必要时创建这些列); 后续行中的每个单元格都属于该行的下一个可用列(考虑 rowspan) 以及跨越所需的任意其他列(必要时创建这些列)。(见 § 3.3 确定行/列网格尺寸)。
总之,表格模型的一个实例由以下内容组成:
CSS 模型并不要求文档语言包含与这些组件一一对应的元素。 对于没有预定义表格元素的文档语言(如 XML 应用), 作者必须将文档语言元素映射为表格元素。 这是通过 display 属性完成的。
以下 display 值会将表格格式化规则分配给任意元素:
- table(等同于 HTML: <table>)
- 指定某个元素定义一个表格, 当置于流布局中时,该表格是块级的。
- inline-table (等同于 HTML:<table>)
- 指定某个元素定义一个表格, 当置于流布局中时,该表格是行级的。
- table-row(等同于 HTML:<tr>)
- 指定某个元素是一行单元格。
- table-row-group (等同于 HTML:<tbody>)
-
指定某个元素对若干行进行分组。
除非另有明确说明,本规范中提到的 table-row-groups 也包括专门的 table-header-groups 和 table-footer-groups。
- table-header-group(等同于 HTML:<thead>)
-
类似于 table-row-group,
但出于布局目的,
第一个这样的行组始终显示在所有其他行和行组之前。
如果一个表格拥有多个
display: table-header-group盒, 只有第一个会被视为表头; 其他的会被视为具有display: table-row-group。 - table-footer-group(等同于 HTML:<tfoot>)
-
类似于 table-row-group,
但出于布局目的,
第一个这样的行组始终显示在所有其他行和行组之后。
如果一个表格拥有多个
display: table-footer-group盒, 只有第一个会被视为表尾; 其他的会被视为具有display: table-row-group。 - table-column (等同于 HTML:<col>)
- 指定某个元素描述一列单元格。
- table-column-group(等同于 HTML:<colgroup>)
- 指定某个元素对一个或多个列进行分组。
- table-cell(等同 于 HTML:<td> 或 <th>)
- 指定某个元素表示表格单元格。
- table-caption (等同于 HTML:<caption>)
- 指定表格的标题。 表格标题定位在表格外边距和边框之间。
注: 替换 元素如果其 display 值为 table-row、table-row-group 、table-header-group、table-footer-group、table-column、table-column-group、table-cell 和 table-caption,则按 CSS Display 3 § 2.4 布局内部显示类型:table-* 和 ruby-* 关键字,会被视为行级 盒;替换元素如果其 display 值为 table 或 inline-table,则按其外部显示类型表现, 如 CSS Display 3 § 2.1 流布局的外部 显示角色:block、inline 和 run-in 关键字所述。这是相对于 CSS 2.1 的破坏性 变更,但与实现相匹配。
2.1.1. 术语
除了表格结构的显示类型外, 本规范还使用以下表述:
- table wrapper box
- 一个围绕 table grid 盒生成的块容器盒,用于计入其拥有的每个table-caption 占据的任何空间。
- table grid box
- 一个包含表格内部盒的块级盒,不包括其标题。
- table-root 元素
- 一个内部显示类型为 table 的元素。
- table-non-root 盒或元素
- 一个proper table child,或一个 table-cell 盒。
- table-track 盒或 元素
- 一个 table-row,或 table-column 盒。
- table-track-group 盒或元素
- 一个 table-row-group,或 table-column-group 盒。
- proper table child 盒或元素
- 一个 table-track-group、table-track,或 table-caption 盒。
- proper table-row parent 盒或元素
- 一个 table-root 或一个 table-row-group 盒。
- table-internal 盒或元素
- 一个 table-cell、table-track 或 table-track-group 盒。
- tabular container
- 一个 table-row 或proper table-row parent 盒。
- 连续 盒
- 两个同级盒如果它们之间没有其他同级盒, 除了可选的、仅包含空白的匿名行内盒之外, 则它们是连续的。 一组同级盒如果序列中的每个盒都与其前一个盒连续, 则该序列是连续的。
- table grid
-
一个矩阵,
其中包含描述 table-root
的所有 table-rows
和 table-cells 位置所需的
尽可能多的行和 列,
这些位置由网格定维算法确定。
网格中的每一行可以对应一个 table-row,每一列可以对应一个 table-column。
- slot of the table grid
-
槽
(r,c)是table grid 中行r与列c相交所创建的可用 空间。table grid 中的每个槽至少被一个 table-cell 覆盖(其中一些 是匿名的),最多被两个覆盖。 table-root 的每个 table-cell 至少覆盖一个槽。
覆盖多个槽的 table-cells 会密集地覆盖这些槽, 这意味着它们所覆盖的槽集合总能描述为一组四个严格正整数
(rowStart, colStart, rowSpan, colSpan),使得槽(r,c)被该 table-cell 覆盖 当且仅当r位于从rowStart(包含)到rowStart+rowSpan(不包含)之间的区间内, 且c位于从colStart(包含)到colStart+colSpan(不包含)之间的区间内;这样的 table-cell 被称为从行
rowStart和列colStart起始。 另外:- 如果某个 table-cell 起始于对应的行(相应地,列),则称它起始于某个 table-row (相应地,table-column)
- 如果某个 table-row-group (相应地,table-column-group)包含该单元格的起始行 (相应地,列),则称该 table-cell 起始于该 table-row-group (相应地,table-column-group)
这样的 table-cell 被称为跨越所有符合上述条件的行
r和列c。 另外:- 如果某个 table-cell 跨越其对应的行(相应地,列),则称它跨越某个 table-row (相应地,table-column)
- 对应于某一行(相应地,列)的 table-row (相应地,table-column)被称为 跨越该行(相应地,列)
- table-row (相应地,table-column)被称为跨越网格的所有列(相应地, 行)
- 包含某一行(相应地,列)的 table-row-group (相应地,table-column)被称为 跨越该行(相应地,列)
- table-row-group (相应地,table-column)被称为跨越网格的所有列(相应地, 行)
2.2. 修正
HTML 以外的文档语言可能不包含 CSS 2.1 表格模型中的所有元素。 在这些情况下,为了让表格模型正常工作,必须假定存在这些“缺失”的元素。
任何 table-internal 元素在必要时都会自动围绕自身生成必要的 匿名表格对象。 table-root 的任何非 table-internal 后代在表格中必须具有一组祖先, 至少包含三个嵌套对象,分别对应于 table/inline-table、 table-row,以及 table-cell。 缺失的盒会导致按以下规则生成匿名盒:
2.2.1. 修正算法
就这些规则而言,脱离流 元素表示为宽度和高度均为零的行内元素。 其包含块也会据此选择。
以下步骤分三个阶段执行:
-
移除无关盒:
以下盒会像display:none一样被丢弃:- table-column 的子项。
- table-column-group 的子项中不是 table-column 的子项。
- 仅包含空白且位于两个直接同级之间的匿名行内盒,其中每个直接同级都是 table-non-root 盒。
-
满足以下所有条件的匿名行内盒:
- 它们只包含空白
- 它们是 tabular container 的第一个和/或最后一个子项
- 其直接同级(如果有)是 table-non-root 盒
-
生成缺失的子包装器:
- 必须围绕 table-root 盒的每一组连续子项生成匿名 table-row 盒, 这些子项不是proper table child 盒。!!测试用例
- 必须围绕 table-row-group 盒的每一组连续子项生成匿名 table-row 盒, 这些子项不是 table-row 盒。!测试用例
- 必须围绕 table-row 盒的每一组连续子项生成匿名 table-cell 盒, 这些子项不是 table-cell 盒。!测试用例
-
生成缺失的父级:
- 必须围绕每一组连续 table-cell 盒 生成匿名 table-row 盒, 这些盒的父级不是 table-row。测试用例
-
必须围绕每一组连续且父级错误的proper table child 盒
生成匿名 table 或 inline-table 盒。
如果该盒的父级是 inline、run-in 或 ruby 盒(或任何会对其子项执行
inlinification 的盒),
则必须生成 inline-table 盒;
否则必须生成 table 盒。
- 如果 table-row 的父级既不是 table-row-group,也不是 table-root 盒, 则该 table-row 的父级错误。
- 如果 table-column 盒的父级既不是 table-column-group 盒,也不是 table-root 盒, 则该 table-column 盒的父级错误。
- 如果 table-row-group、table-column-group 或 table-caption 盒的父级不是 table-root 盒, 则其父级错误。
- 必须围绕每个 table-root 生成匿名 table-wrapper 盒。
对于 inline-table
盒,其显示类型为
inline-block; 对于 table 盒,其显示类型为 block。 table wrapper 盒会建立块格式化上下文。 当对 inline-table 进行基线垂直对齐时, 使用的是 table-root 盒(而不是 table-wrapper 盒)。 table-wrapper 盒的宽度是其内部 table grid 盒的 border-edge 宽度。 那些会依赖 table-wrapper 盒尺寸上的 width 和 height 的百分比,改为相对于 table-wrapper 盒的包含块,而不是 table-wrapper 盒自身。
测试
- anonymous-table-ws-001.html (实时 测试) (源代码)
- fixup-dynamic-anonymous-inline-table-001.html (实时 测试) (源代码)
- fixup-dynamic-anonymous-inline-table-002.html (实时 测试) (源代码)
- fixup-dynamic-anonymous-inline-table-003.html (实时 测试) (源代码)
- fixup-dynamic-anonymous-table-001.html (实时 测试) (源代码)
- table-model-fixup.html (实时测试) (源代码)
2.2.2. 修正盒的特征
除了其显示类型之外,为修正目的创建的匿名盒 不会接收任何特定样式或默认样式, 除非本规范另有说明。
这特别意味着 它们的计算背景为“transparent”, 它们的计算内边距为“0px”, 它们的计算 border-style 为“none”。
还值得提醒的是,匿名盒 会通过盒树继承属性值。
2.2.3. 示例
< div class = "row" > < div class = "cell" > George</ div > < div class = "cell" > 4287</ div > < div class = "cell" > 1998</ div > </ div >
以下是关联的样式:
.row{ display : table-row} .cell{ display : table-cell}
修正后,这会生成布局盒,就好像初始 HTML 如下:
< table > < tr > < td > George</ td > < td > 4287</ td > < td > 1998</ td > </ tr > </ table >
在此示例中,假定三个 table-cell
匿名盒包含各行中的文本。具有 display: table-row 的 div 内的文本会被封装到匿名行内盒中,
如视觉
格式化模型中所述:
< div class = "inline-table" > < div class = "row" > 这是顶部行。</ div > < div class = "row" > 这是中间行。</ div > < div class = "row" > 这是底部行。</ div > </ div >
.inline-table{ display : inline-table; } .row{ display : table-row; }
这会生成布局盒,就好像初始 HTML 如下:
< table > < tr > < td > 这是顶部行。</ td > </ tr > < tr > < td > 这是中间行。</ td > </ tr > < tr > < td > 这是底部行。</ td > </ tr > </ table >
3. 布局
3.1. 核心布局原则
不同于其他块级盒,表格默认不会填满其包含块。
当它们的 width 计算为
auto 时,它们的行为就好像指定了 fit-content 一样。
这不同于大多数块级盒,后者的行为就好像指定了 stretch 一样。
表格的 min-content width 是 适应其所有列的 min-content 宽度以及其不可分配空间所需的宽度。
表格的 max-content width 是 适应其所有列的 max-content 宽度以及其不可分配空间所需的宽度。
如果分配给表格的宽度大于其 min-content width, 可用宽度分配算法 将相应地调整列宽。
本节覆盖其他规范中描述的适用于计算宽度的一般用途规则。
特别是,如果表格的外边距设为 0 且宽度设为 auto,
该表格不会自动调整大小以填满其包含块。
然而,一旦找到表格 width 的使用值(使用下方给出的算法),
这些规则的其他部分仍然适用。
因此,例如可以使用左右 auto 外边距使表格居中。
3.2. 表格布局算法
要布局一个表格,用户代理必须应用以下操作:
- 确定表格所需的行/列数量。
这是通过执行 § 3.3 确定行/列网格尺寸 中描述的步骤完成的。 -
[A] 如果行/列网格至少有一个槽:
- 确保每个单元格槽至少被一个单元格占据。
这是通过执行 § 3.4 缺失单元格修正 中描述的步骤完成的。 - 计算每列的最小宽度。
这是通过执行 § 3.8 计算表格度量 中描述的步骤完成的。 - 计算表格的宽度。
这是通过执行 § 3.9.1 计算表格宽度 中描述的步骤完成的。 - 在各列之间分配表格的宽度。
这是通过执行 § 3.9.3 分配算法 中描述的步骤完成的。 - 计算表格的高度。
这是通过执行 § 3.10.1 计算表格高度 中描述的步骤完成的。 - 在各行之间分配表格的高度。
这是通过执行 § 3.10.5 分配算法 中描述的步骤完成的。
[B] 否则,布局一个空 表格:
- 计算表格的宽度。
这是通过返回 CAPMIN 与表格网格盒计算宽度 (包括边框和内边距)中的较大值完成的, 如果后者是确定的;否则使用零。 - 计算表格的高度。
这是通过返回所有 table-caption 高度之和 (其宽度被设为表格宽度, 并适当考虑外边距) 与表格网格盒的计算高度(包括边框和内边距)之和完成的, 如果后者是确定的;否则使用零。
- 确保每个单元格槽至少被一个单元格占据。
- 为每个 table-caption 和 table-cell 分配其位置和尺寸。
这是通过运行 § 3.11 单元格、标题和其他内部 表格盒的定位中的步骤完成的。
下列示意图以另一种方式描述该算法, 以便更容易理解。
3.3. 确定行/列网格尺寸
如表格结构一节所述, 表格具有多少行和列 可以从表格结构确定。 确定行/列网格尺寸,以及在该网格中为 table-cells 分配其 slot 都需要运行用于表格的 HTML 算法。
3.3.1. HTML 算法
不源自与其 display 类型等效的 HTML 表格元素的 CSS 盒, 需要先转换为其 HTML 等效物,然后才能应用该算法,见下文。 在本规范的这个级别中,无法仅用 CSS 指定单元格的跨越, 要做到这一点,需要使用 HTML td 元素。
应用 HTML5 表格格式化 算法, 其中盒表现得像与其 display 类型等效的 HTML 元素, 并且当且仅当其来源元素是相同类型的 HTML 元素时,使用其来源元素的属性 (否则,它们表现得就像没有任何属性一样)。
< ul class = "table" > < li >< b > 一</ b >< i > 1</ i ></ li > < li >< b > 二</ b >< i > 2</ i ></ li > < li >< b > 三</ b >< i > 3</ i ></ li > </ ul > < style > ul . table { display : table ; } ul . table > li { display : table-row ; } ul . table > li > * { display : table-cell ; } </ style >
产生的行/列网格与以下内容相同
< table >< tbody > < tr > < td ></ td > < td ></ td > </ tr > < tr > < td ></ td > < td ></ td > </ tr > < tr > < td ></ td > < td ></ td > </ tr > </ tbody ></ table >
<!-- 使用 dom api 构建,因为这会被 html 解析器修正 -->
< grid style = "display: table" > < row style = "display: table-row" > < th rowspan = "2" > 1</ th > < colgroup style = "display: table-cell" span = "2" colspan = "2" > 2</ colgroup > </ row > < tr > < td > A</ td > < td > B</ td > < td > C</ td > </ tr > </ grid >
产生的行/列网格与以下内容相同
< table > < tr > < th rowspan = "2" > 1</ th > < td > 2</ td > </ tr > < tr > < td > A</ td > < td > B</ td > < td > C</ td > </ tr > </ table >
请注意,第一行的第二个单元格没有应用 ```colspan=2```,因为其来源 元素不是 HTML TD 元素。
测试
3.3.2. 轨道合并
HTML 表格格式化算法有时会生成比正确布局表格所需更多的轨道。 这些轨道历来被用户代理忽略, 因此下一步会将它们完全移除,以避免在规范后续部分将它们作为例外处理。 我们试图通过这一更改保持功能不变,但如果你偶然发现 此更改导致的任何问题,请提交问题。
通过如下方式合并连续轨道,迭代地修改获得的网格: 只要获得的行/列网格中存在一个合格轨道,使得 没有 table-column/table-row 盒显式定义该轨道,并且 该轨道和前一个轨道被完全相同的一组单元格跨越, 则为了计算表格布局,必须将这两个轨道合并为一个单一轨道。 将跨越被删除轨道的单元格的跨越减一以进行补偿, 并在需要时类似地移动单元格起始所在的轨道。(见 spanning-ghost-rows 测试用例)
对于自动模式下的表格, 就轨道合并算法而言,每个轨道都是一个合格 轨道。 对于固定模式下的表格, 只有行有资格以这种方式合并;这意味着每一列都会被保留。
最后,为 table-root 网格分配其正确的行数和列数(来自其 映射元素), 并为每个 table-cell 分配其准确的 rowStart/colStart/rowSpan/colSpan(来自其映射元素)。
3.4. 缺失单元格修正
下节澄清并扩展 CSS 2.1 的陈述,即 缺失单元格会被渲染得好像一个匿名 table-cell 盒占据了它们在网格中的位置 (“缺失单元格”是行/列网格中尚未被任何 table-cell 盒覆盖的槽)。
一旦表格中的列数已知,任何 table-row 盒都必须被修改,使其 拥有足够的单元格来填满表格的所有列,同时考虑跨越。 新的 table-cell 匿名盒必须追加到其行内容中,直到满足此 条件。
测试
3.5. 表格布局模式
本节涵盖会修改表格布局方式的标志。 表格布局有三个主要标志:table-layout、border-collapse 和 caption-side。 border-collapse 标志有一个可选的 border-spacing 参数。
3.5.1. table-layout 属性
| 名称: | table-layout |
|---|---|
| 值: | auto | fixed |
| 初始值: | auto |
| 适用于: | table grid 盒 |
| 继承: | 否 |
| 百分比: | 不适用 |
| 计算值: | 指定的关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
测试
每当 table-layout 属性的计算值等于 fixed,
且表格根的指定宽度为
<length-percentage>、min-content 或 fit-content 时,
table-root 就被称为以固定模式布局。
当指定宽度不是这些值之一,
或者 table-layout 属性的计算值为 auto
时,
table-root 就被称为以自动模式布局。
当 table-root 以固定 模式布局时, 在宽度计算中会忽略其 table-cells 的内容, 列尺寸的聚合算法只考虑属于第一行轨道的 table-cells, 因此布局只取决于为表格的 table-columns 或第一行单元格显式指定的值; 具有不确定宽度的列会在考虑具有确定宽度的列之后, 获得剩余空间的公平份额;如果没有剩余空间则为 0px (见 § 3.8.3 计算列度量)。
3.5.2. border-collapse 属性
| 名称: | border-collapse |
|---|---|
| 值: | separate | collapse |
| 初始值: | separate |
| 适用于: | table grid 盒 |
| 继承: | 是 |
| 百分比: | 不适用 |
| 计算值: | 指定的关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
当 border-collapse 属性的值为 collapse 时,
相邻单元格的边框会合并在一起,使每个单元格只绘制共享边框的一半。
因此,其他一些属性(如 border-spacing)在此情况下不会应用
(见 § 3.6.2 适用于边框合并
模式的覆盖),
(见 § 3.7 边框合并)。
在这种情况下,table-root 被称为以边框合并 模式布局。 否则,table-root 被称为以边框分离 模式布局。
测试
- background-clip-001.html (实时测试) (源代码)
- box-shadow-001.html (实时测试) (源代码)
- collapsed-border-color-change-with-compositing.html (实时 测试) (源代码)
- collapsed-border-remove-row-group.html (实时 测试) (源代码)
- collapsed-scroll-overflow.html (实时 测试) (源代码)
- border-collapse-computed.html (实时 测试) (源代码)
- border-collapse-invalid.html (实时 测试) (源代码)
- border-collapse-valid.html (实时 测试) (源代码)
3.5.2.1. border-spacing 属性
| 名称: | border-spacing |
|---|---|
| 值: | <length>{1,2} |
| 初始值: | 0px 0px |
| 适用于: | table grid 盒, 当 border-collapse 为 separate 时 |
| 继承: | 是 |
| 百分比: | 不适用 |
| 计算值: | 两个绝对长度 |
| 规范顺序: | 按语法 |
| 动画类型: | 按计算值 |
这些长度指定边框分离模式中 相邻单元格边框之间的距离, 且不得严格为负。
如果指定一个长度,它同时给出水平和垂直间距。 如果指定两个长度,第一个给出水平间距,第二个给出垂直间距。
有关这如何影响表格布局的详细信息,见 § 3.8.1 计算不可分配空间。
测试
3.5.3. caption-side 属性
| 名称: | caption-side |
|---|---|
| 值: | top | bottom |
| 初始值: | top |
| 适用于: | table-caption 盒 |
| 继承: | 是 |
| 百分比: | 不适用 |
| 计算值: | 指定的关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
测试
此属性指定 caption 盒相对于 table grid 盒的位置。 各值含义如下:
- top
- 将 caption 盒定位在 table grid 盒上方。
- bottom
- 将 caption 盒定位在 table grid 盒下方。
要在 caption 盒内水平对齐标题内容,请使用 text-align 属性。
在此示例中,caption-side 属性将标题放置在表格下方。 标题将与表格的父级一样宽,并且标题文本将左对齐。
caption {
caption-side: bottom;
width: auto;
text-align: left
}
3.6. 样式覆盖
某些 css 属性在 css 表格内部的行为有所不同。 以下各节列出这些例外及其效果。
3.6.1. 适用于所有模式的覆盖
以下规则适用于所有表格,无论使用哪种布局模式。
-
表格上的属性 position、float、
margin-*、top、right、
bottom
和 left
的计算值
会用于 table-wrapper 盒,而不是 table grid 盒;
对于其使用可能会强制 transform-style 的使用值为
flat的属性 (见列表)以及它们相关的 简写/长写属性,也是如此: 该列表目前包括 overflow、opacity、 filter、clip、clip-path、isolation、mask-*、mix-blend-mode、transform-* 和 perspective。
当指定值未应用于 table grid 和/或 wrapper 盒时, 会改为对该盒使用未设置值(根据属性为 inherit 或 initial)。 - overflow 属性在 table-root 和 table-wrapper
盒上,当其值既不是
visible、clip也不是hidden时, 会被忽略,并被视为其值为visible。 - table-column 和 table-column-group 盒的所有 css 属性都会被忽略, 除非本规范明确指定。
- margin、padding、overflow 和 z-index 在 table-track 和 table-track-group 盒上会被忽略。
- margin 在 table-cell 盒上会被忽略 (就好像它被设为 0px)。
- background 在 table-cell 盒上 使用 § 5.3.2 绘制单元格背景中描述的特殊背景绘制算法进行绘制。
3.6.2. 适用于边框合并模式的覆盖
当表格以边框合并模式布局时,适用以下规则:
- padding 在 table-root 上会被忽略(就好像它被设为 0px)。
- border-spacing 在 table-root 上会被忽略(就好像它被 设为 0px)。
- border-radius 在 table-root 和 table-non-root 盒上 都会被忽略(就好像它被设为 0px)。
- table-root 及其拥有的 table-cell 盒的边框布局和渲染所使用的值, 通过 § 3.7 边框合并中描述的特殊冲突解决算法确定。
3.7. 边框合并
整个本节是一项提案,旨在使合并边框的渲染更加合理。 由于实现差异非常明显,预计它会比其他一些部分需要更多讨论。 由于浏览器处理这一点的方式差异很大,如果不重新实现,就无法实现收敛。 本提案的一个主要关注点是支持尽可能多的情况,同时 将新表格实现所需的工作量保持得尽可能低。
背景: CSS+HTML 允许表格交汇处的边框模式出现前所未有的组合, 这使得正确支持所有情况变得困难; 事实上,某些组合并不是适定 问题, 因此没有任何渲染算法能够达到最优。
由于它们从简单事物(HTML)发展到非常复杂的事物(HTML+CSS), Web 浏览器当前使用的表格渲染模型(背景和边框)是不合理的 (意思是它们有缺陷、不可互操作,且完全不 CSS 化)。 许多通常的 CSS 假设被破坏,渲染结果存在巨大差异。
本提案旨在修复这一状况。
测试
- border-collapse-double-border.html (实时 测试) (源代码)
- border-collapse-dynamic-col-001.html (实时 测试) (源代码)
- border-collapse-dynamic-oof.html (实时 测试) (源代码)
- border-collapse-dynamic-section.html (实时 测试) (源代码)
- border-collapse-empty-cell.html (实时 测试) (源代码)
- border-collapse-rowspan-cell.html (实时 测试) (源代码)
- collapsed-border-remove-cell.html (实时 测试) (源代码)
border-collapsing 相对于 2.1 的破坏性变更 [Issue #604]
3.7.1. 合并边框的冲突解决
当它们以边框合并模式布局时,共享边框的 table-root 和 table-cell 盒会尝试统一 它们的边框, 使其使用相同的样式、宽度和颜色进行渲染(只要可能)。 这是通过运行以下算法完成的。
3.7.1.1. 合并 边框的冲突解决算法
对于 table-root 的任何 table-cell C°:
-
解决与 border-right 的冲突:
- 令 S 为 table-cell 边框样式的有序集合,按单元格的 RowStart/ColumnStart 顺序排序; 初始时,令 S 仅包含 C° 的 border-right 样式
- 向集合 S 添加所有与 C° 的右边框共享其左边框某一段的单元格的 border-left 样式
- 重复以下两条指令,直到没有新的边框样式被添加到 S:
- 协调 S 中存在冲突的边框
-
解决与 border-bottom 的冲突:
- 令 S 为 table-cell 边框样式的有序集合,按单元格的 RowStart/ColumnStart 顺序排序; 初始时,令 S 仅包含 C° 的 border-bottom 样式
- 向集合 S 添加所有与 C° 的下边框共享其上边框某一段的单元格的 border-top 样式
- 重复以下两条指令,直到没有新的边框样式被添加到 S:
- 协调 S 中存在冲突的边框
-
将所有边框的使用宽度除以二。
在渲染时,只要需要,此效果都会得到补偿, 但为了布局正确性,这是必需的。(见 § 5.3.3.1 边框合并模式下的变化)
然后,对于该 table-root:
- 将 table-root 的
border-{top,bottom,left,right}
与形成表格边界的所有单元格的对应边框进行协调(彼此独立),
但不实际修改 table-root 的边框属性。
如果表格和单元格的边框 样式具有相同的优先级, 保留单元格边框样式。
完成后,将 table-root 的 border-{…}-width 设为该边框在协调过程中找到的最大宽度的一半, 然后将 border-{…}-style 设为 solid,并将 border-{…}-color 设为 transparent。
https://jsfiddle.net/bn3d1sm4/
https://jsfiddle.net/bn3d1sm4/1/
https://jsfiddle.net/bn3d1sm4/2/
…
https://jsfiddle.net/bn3d1sm4/15/
测试
3.7.1.2. 合并 边框的协调算法
更改合并边框协调中的 优先级?[Issue #606]
给定一个边框样式的有序集合(BC1, BC2, … 位于单元格 C1, C2, … 中), 执行以下算法,以确定这些冲突边框的边框属性使用值。
- 将 CurrentlyWinningBorderProperties 设置为 “border: 0px none transparent”
-
对每个边框 BCi:
- 考虑 BCi 边框的属性
-
如果该边框分隔两列:
- 对每个边框 BCi: 对 Ci 单元格跨越的每个 table-column(如果有)。 考虑 table-column 中会与 BCi 连续绘制的任何边框的边框属性。
- 对每个边框 BCi: 对包含 Ci 单元格所跨越列的每个 table-column-group(如果有)。 考虑 table-column-group 中会与 BCi 连续绘制的任何边框的边框属性。
-
如果该边框分隔两行:
- 对每个边框 BCi: 对 Ci 单元格跨越的每个 table-row(如果有)。 考虑 table-column 中会与 BCi 连续绘制的任何边框的边框属性。
- 对每个边框 BCi: 对包含 Ci 单元格所跨越列的每个 table-row-group(如果有)。 考虑 table-row-group 中会与 BCi 连续绘制的任何边框的边框属性。
- 返回 CurrentlyWinningBorderProperties
3.7.1.3. 边框样式的优先级
给定两个边框样式,具有最高优先级的边框样式是符合以下条件的边框样式……
- ……如果只有一个边框样式的 border-style 值为 "hidden",则为该边框样式
- ……一旦转换为 css 像素后,具有最大的 border-width
-
……具有在以下列表中最先出现的 border-style:
double, solid, dashed, dotted, ridge, outset, groove, inset, none
如果这些条件都不匹配,则两个边框具有相同的优先级。
3.8. 计算表格度量
测试
3.8.1. 计算不可分配空间
表格的不可分配 空间是 连续 table-cells 的边框之间距离 (以及 table-root 的边框与 table-cells 之间距离)的总和。
两个连续 table-cells 的边框之间的距离是 border-spacing,如果存在的话。
表格边框与 表格边缘单元格的边框之间的距离 是 表格在该侧的内边距, 加上相关的边框间距距离(如果有)。
3.8.2. 计算单元格度量
以下术语是表格或表格单元格的参数。 这些参数封装了具有不同 border-collapse 值(separate 或 collapse)的表格之间的差异, 从而使本节余下的小节不需要以不同方式引用它们。
- cell intrinsic offsets
- cell intrinsic offsets 是用于捕获表格单元格中与固有宽度计算相关的内边距和边框部分的术语。 它是一组用于 border-left-width、padding-left、padding-right 和 border-right-width 的计算值 (以及 margin-left 和 margin-right 的零值), 定义如下:
- table intrinsic offsets
-
table intrinsic offsets 捕获表格中与固有宽度计算相关的内边距和边框部分。
它是一组用于 border-left-width、padding-left、padding-right 和
border-right-width 的计算值
(以及 margin-left 和 margin-right 的零值),
定义如下:
外边距不包含在 table intrinsic offsets 中,因为外边距的处理取决于 caption-side 属性。
在边框合并模式中 intrinsic offsets 的处理 [Issue #608]
- 总 水平边框间距
-
总水平边框间距为每个表格定义如下:
- 对于以边框分离模式布局且至少包含 一列的表格, border-spacing 属性计算值的水平分量乘以表格中的列数加一
- 否则为 0
- offsets-adjusted min-width、width 和 max-width
-
- 对于 table-track 和 table-track-group 盒, 宽度属性的 offsets-adjusted 值是其计算值, 不论应用在该元素上的 box-sizing 值为何。
- 对于 table-cell 盒, 宽度属性的 offsets-adjusted 值是其计算值, 并且根据 box-sizing 的值,最终从中扣除单元格的 border-{left|right}-width 和/或 padding-{left|right}。
- outer min-content 和 outer max-content 宽度
-
outer min-content 和 max-content 宽度为表格单元格、列和列组定义。
这些定义中使用的 width、min-width 和 max-width 值是上文定义的 offsets-adjusted 值:
- table-cell 的 outer min-content width 是
max(min-width, min-content width)并按 cell intrinsic offsets 进行调整。 - table-column 或 table-column-group 的 outer min-content width 是
max(min-width, width)。 - 位于非受约束列中的
table-cell 的
outer max-content width 是
max(min-width, width, min-content width, min(max-width, max-content width))并按 cell intrinsic offsets 进行调整。 - 位于受约束列中的
table-cell 的
outer max-content width 是
max(min-width, width, min-content width, min(max-width, width))并按 cell intrinsic offsets 进行调整。 - table-column 或 table-column-group 的 outer max-content width 是
max(min-width, min(max-width, width))。
- table-cell 的 outer min-content width 是
- percentage contributions
-
表格单元格、列或列组的百分比贡献
根据 width 和
max-width 的计算值定义,这些计算值为百分比:
min(percentage width, percentage max-width)。
如果计算值不是百分比, 则对 width 使用0%, 对 max-width 使用一个infinite百分比。
3.8.3. 计算列度量
本小节定义与表格每一列相关联的三个重要值: 其 min-content width(归属于该列的最小可能宽度), 其 max-content width(如果没有其他约束适用时,将归属于该列的宽度), 其 intrinsic percentage width(该列希望获得的表格宽度百分比,并且最终可能覆盖其 max-content 宽度)。
为了计算这些值,会使用一个迭代算法。 首先,忽略任何跨越多于一列的单元格来计算这些值。 然后,通过逐步考虑跨越更多列的单元格来更新这些值。 当已经考虑了跨越表格所有列的单元格时,该算法结束,并最终确定这些值。
就以固定模式布局时测量列而言, 仅考虑在表格第一行中起始的单元格 (在重新排序表头和表尾之后), 如果有的话。 此外,单元格的 min-content 和 max-content 宽度 被视为零,除非它们直接被指定 为 length-percentage,在这种情况下,它们会 基于表格宽度解析(如果表格宽度是确定的,否则使用 0)。
就计算单元格的 outer min-content width 而言, 表格单元格中其宽度依赖于父单元格宽度百分比的后代 会被视为具有 auto 宽度。测试用例 测试用例
- 基于 span 至多为 1 的单元格的列 min-content width
-
以下各项中的最大值:
-
为该列指定的宽度:
- 其对应 table-column 的 outer min-content 宽度,如果有(且不是 auto)
- 其对应 table-column-group 的 outer min-content 宽度,如果有
- 或者 0,如果都没有
- 每个跨越该列且其 colSpan 为 1 的单元格的 outer min-content 宽度 (或者在固定模式中仅第一行中的那个) 如果没有则为 0
-
为该列指定的宽度:
- 基于 span 至多为 1 的单元格的列 max-content width
-
以下各项中的最大值:
- 其对应 table-column-group 的 outer max-content 宽度,如果有
- 其对应 table-column 的 outer max-content 宽度,如果有
- 每个跨越该列且其 colSpan 为 1 的单元格的 outer max-content 宽度 (或者如果处于固定模式,则仅第一行中的那个) 如果没有这样的单元格则为 0
- 基于 span 至多为 1 的单元格的列 intrinsic percentage width
- 以下各项的百分比贡献中的最大值: 每个跨越该列且其 colSpan 为 1 的单元格、 其对应 table-column(如果有),以及 其对应 table-column-group(如果有)
- 基于 span 至多为 N 的单元格的列 min-content width(N > 1)
-
基于 span 至多为 N-1 的列 min-content width 与该列中 colSpan
为 N 的单元格贡献值中的最大值,
其中单元格的贡献值是执行以下步骤得到的结果:
- 将基准 min-content width 定义为 该单元格跨越的所有列 基于 span 至多为 N-1 的 max-content 宽度之和。
- 将基准 border spacing 定义为 该单元格跨越的任何列的水平 border-spacing 之和, 不包括该单元格起始所在的列。
-
单元格的贡献值为以下各项之和:
- 基于 span 至多为 N-1 的列 min-content width
-
以下乘积:
-
以下比率:
- 该列基于 span 至多为 N-1 的 max-content width 减去该列基于 span 至多为 N-1 的 min-content width,与
- 基准 max-content width 减去基准 min-content width
- 单元格的 outer min-content width 减去基准 min-content width 和基准 border spacing, 并钳制为至少 0、至多为 基准 max-content width 与基准 min-content width 之间的差
-
以下比率:
-
以下乘积:
- 该列基于 span 至多为 N-1 的 max-content width 与基准 max-content width 的比率
- 单元格的 outer min-content width 减去基准 max-content width 和基准 border spacing, 如果这是负数则为 0
- 基于 span 至多为 N 的单元格的列 max-content width(N > 1)
-
基于 span 至多为 N-1 的 max-content width
与该列中 colSpan 为 N 的单元格贡献值中的最大值,
其中单元格的贡献值是执行以下步骤得到的结果:
- 将基准 max-content width 定义为 该单元格跨越的所有列 基于 span 至多为 N-1 的 max-content 宽度之和。
- 将基准 border spacing 定义为 该单元格跨越的任何列的水平 border-spacing 之和, 不包括该单元格起始所在的列。
-
单元格的贡献值为以下各项之和:
- 基于 span 至多为 N-1 的列 max-content width
-
以下乘积:
- 该列基于 span 至多为 N-1 的 max-content width 与基准 max-content width 的比率
- 单元格的 outer max-content width 减去基准 max-content width 和基准 border spacing, 如果这是负数则为 0
- 基于 span 至多为 N 的单元格的列 intrinsic percentage width(N > 1)
-
如果基于 span 至多为 N-1 的列 intrinsic percentage width 大于 0%,
则基于 span 至多为 N 的列 intrinsic percentage width
与基于 span 至多为 N-1 的列 intrinsic percentage width 相同。
否则,它是该列中 colSpan 为 N 的单元格贡献值中的最大值, 其中单元格的贡献值是执行以下步骤得到的结果:- 从该单元格的百分比贡献开始。
- 减去该单元格跨越的所有列基于 span 至多为 N-1 的 intrinsic percentage width。 如果这得到负结果,则将其改为 0%。
-
乘以以下比率:
- 该列的 non-spanning max-content width 与
- 该单元格跨越的所有列中,基于 span 至多为 N-1 的列 intrinsic percentage width 等于 0% 的列的 non-spanning max-content width 之和。
- 列的 min-content width
- 基于 span 至多为 N 的列 min-content width,其中 N 是表格中的列数
- 列的 max-content width
- 基于 span 至多为 N 的列 max-content width,其中 N 是表格中的列数
- 列的 intrinsic percentage width
-
以下两项中的较小者:
- 基于 span 至多为 N 的列 intrinsic percentage width, 其中 N 是表格中的列数
- 100% 减去表格中所有先前列的 intrinsic percentage width 之和 (当 direction 为 "ltr" 时在更左侧("rtl" 时为右侧))测试用例
将列的 intrinsic percentage widths 总和钳制到最大 100% 意味着表格布局算法在切换列时并非不变。
- 受约束性
- 如果某列的对应 table-column-group(如果有)、 其对应 table-column(如果有), 或任何仅跨越该列的单元格 具有一个计算 width, 其值不是 "auto", 且不是百分比,则该列是受约束的。
测试
- computing-row-measure-0.html (实时 测试) (源代码)
- computing-row-measure-1.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-children.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-replaced-children-001.html (实时 测试) (源代码)
- computing-column-measure-0.html (实时 测试) (源代码)
- computing-column-measure-1.html (实时 测试) (源代码)
- computing-column-measure-2.html (实时 测试) (源代码)
3.9. 可用宽度分配
测试
- distribution-algo-1.html (实时 测试) (源代码)
- distribution-algo-2.html (实时 测试) (源代码)
- distribution-algo-min-content-guess.html (实时 测试) (源代码)
- distribution-algo-min-content-percent-guess.html (实时 测试) (源代码)
- distribution-algo-min-content-specified-guess.1.html (实时 测试) (源代码)
- distribution-algo-min-content-specified-guess.html (实时 测试) (源代码)
- td-with-subpixel-padding-vertical-rl.html (实时 测试) (源代码)
- td-with-subpixel-padding.html (实时 测试) (源代码)
3.9.1. 计算表格宽度
在决定所有列的最终宽度之前, 有必要计算表格本身的宽度。
如前所述,这通常会是所有列首选宽度之和, 加上任何额外部分。 在这种情况下,宽度分配将使每列获得其首选宽度。 然而,有一些情况中作者显式要求某个其他宽度, 也有一些情况中表格无法获得其所需的宽度。
标题宽度最小值 (CAPMIN)是 table captions 的 min-content contribution 中的最大值。
行/列网格宽度最小值 (GRIDMIN)宽度是 所有列的 min-content width 之和 加上单元格间距或边框。
行/列网格宽度最大值 (GRIDMAX)宽度是 所有列的 max-content width 之和 加上单元格间距或边框。
表格的使用 min-width是 已解析的 min-width、CAPMIN 和 GRIDMIN 中较大的值。
表格的使用宽度 取决于列和标题宽度,如下所示:
-
如果 table-root 的
width
属性具有一个不同于
auto的计算值 (解析为 resolved-table-width), 则使用宽度为 resolved-table-width 与 表格的使用 min-width 中较大的值。如果使用宽度大于 GRIDMIN, 额外宽度应分配给各列。见 § 3.9 可用宽度分配。 - 如果 table-root 具有 'width: auto', 则使用宽度为 min(GRIDMAX, 表格的包含块宽度) 与表格的使用 min-width 中较大的值。
可分配表格 宽度是 表格的使用宽度 减去总水平边框间距(如果有)。 这是我们将能够分配给各列的宽度。
测试
- visibility-collapse-col-001.html (实时 测试) (源代码)
- visibility-collapse-col-002.html (实时 测试) (源代码)
- visibility-collapse-col-003.html (实时 测试) (源代码)
- visibility-collapse-col-004-dynamic.html (实时 测试) (源代码)
- visibility-collapse-col-005.html (实时 测试) (源代码)
- visibility-collapse-colspan-001.html (实时 测试) (源代码)
- visibility-collapse-colspan-002.html (实时 测试) (源代码)
- visibility-hidden-col-001.html (实时 测试) (源代码)
- visibility-hidden-nested-001.html (实时 测试) (源代码)
- visibility-hidden-nested-002.html (实时 测试) (源代码)
- computing-table-width-0.html (实时 测试) (源代码)
- computing-table-width-1.html (实时 测试) (源代码)
- table-intrinsic-size-001.html (实时 测试) (源代码)
- table-intrinsic-size-002.html (实时 测试) (源代码)
- table-intrinsic-size-003.html (实时 测试) (源代码)
- table-intrinsic-size-004.html (实时 测试) (源代码)
3.9.2. 核心分配原则
本节为非规范性内容。
3.9.2.1. 规则
理想情况下,每列都应获得其首选宽度(通常是其 max-content width)。 然而,先前计算出的可分配表格宽度 可能过大或过小,无法实现此结果, 在这种情况下,用户代理必须按照宽度分配算法中的描述, 为各列分配临时宽度。
此算法在确定列的使用宽度时遵循三条规则:
规则 0: 在固定模式中, auto 列和百分比列会被分配零像素的最小宽度,并且 百分比解析遵循一套不同的规则, 其目标是确保像素列始终被分配其首选宽度。
规则 1: 分配首选宽度时, 指定百分比的列优先级高于 指定单位值的列,而指定单位值的列优先级又高于 auto 列。
规则 2: 使用相同尺寸类型的列(百分比列、像素列或
auto 列)遵循相同的分配方法。
例如,它们都获得其 min-content width,或者都获得其 max-content
width。
此规则有一个例外。
当向百分比列给予其首选百分比宽度时,
如果这会导致尺寸小于其 min-content width,
则该列会改为被分配其 min-content width,
尽管百分比列组整体仍被视为
已分配首选百分比宽度。
规则 3: 分配给所有列的宽度之和应等于可分配 表格宽度。
3.9.2.2. 可用尺寸
所有三种类型的列都有以下可能的使用宽度。
- min-content width:
适应该列内容所需的尺寸 - min-content width + delta:
介于 min-content 宽度和首选 宽度之间的值 - 首选宽度:
为该列指定的尺寸, 或在不换行的情况下适应该列内容所需的尺寸 - 首选宽度 + delta
大于首选宽度的值
分配算法定义这些值,并说明何时使用它们。
3.9.3. 分配算法
当表格以给定的使用宽度布局时, 每列的使用宽度必须按如下方式确定, 最终还要考虑对此算法的变更,这些变更适用于固定模式。
首先,为表格的每一列分配一个尺寸类型:
-
percent-column:
任一约束被定义为仅使用百分比 (且值不同于 0%)的列 -
pixel-column:
任一约束被定义为仅使用 已定义长度(且不是 percent-column)的列 -
auto-column:
任何其他列
然后,按尺寸类型为各列分配有效的尺寸方法,得到以下 sizing-guesses:
- min-content sizing-guess 是一组列宽分配,其中 每列都被分配其 min-content width。
-
min-content-percentage sizing-guess 是一组
列宽分配,其中:
-
每个 percent-column 被分配以下两项中的较大者:
- 其 intrinsic percentage width 乘以可分配宽度,以及
- 其 min-content width。
- 所有其他列都被分配其 min-content width。
-
每个 percent-column 被分配以下两项中的较大者:
-
min-content-specified sizing-guess 是一组
列宽分配,其中:
-
每个 percent-column 被分配以下两项中的较大者:
- 其 intrinsic percentage width 乘以可分配宽度,以及
- 其 min-content width
- 任何其他受约束列被分配其 max-content width
- 所有其他列都被分配其 min-content width。
-
每个 percent-column 被分配以下两项中的较大者:
-
max-content
sizing-guess 是一组列宽分配,其中:
-
每个 percent-column 被分配以下两项中的较大者:
- 其 intrinsic percentage width 乘以可分配宽度,以及
- 其 min-content width
- 所有其他列都被分配其 max-content width。
-
每个 percent-column 被分配以下两项中的较大者:
-
可分配表格宽度始终大于或等于 由 min-content sizing-guess 得出的表格宽度。
-
四种 sizing-guesses (min-content、min-content-percentage、min-content-specified 和 max-content) 中每列的宽度按非递减顺序排列。
如果可分配 表格宽度小于或等于 max-content sizing-guess, 则列的使用宽度必须是两个相邻 sizing-guesses 的线性组合(权重之和为 1), 这两个 sizing-guesses 的宽度总和界定了可用宽度。
否则,列的使用宽度是从 max-content sizing-guess 开始,并根据将多余宽度分配给列(用于使用宽度)的规则, 将多余宽度分配给表格各列所得的结果。
下列示意图以另一种方式描述该算法, 以便更容易理解。
图例
尺寸算法: 表格的每个图形都表示一种确定列尺寸的方式。 左侧四种情况是规范中上文描述的 sizing-guesses:min-content、min-content-percentage、min-content-specified 和 max-content。 右侧情况是可用尺寸与四种 sizing-guesses 中任一种都不完全匹配时所需的插值。
尺寸方法的选择: 尺寸方法选择始终从 min-content sizing-guess(左上)开始, 然后通过比较可用宽度与当前使用的方法所消耗的宽度继续。 绿色箭头表示如果应用当前方法后还有额外空间要分配, 你应遵循的方向。 红色箭头表示如果应用当前方法分配了过多空间,需要回退时 你应遵循的方向。
列类型: 每种列类型(auto、px、%)在示意图中都有自己的颜色(黄色、 蓝色、橙色)。 在插值中: 从其在先前 sizing-guess 中的尺寸收缩的列会重新绘制为红色,而 从其在先前 sizing-guess 中的尺寸扩展的列会重新绘制为绿色。
3.9.3.1. 固定模式下宽度分配的变化
以下对前述算法的变更适用于固定模式:
-
percent-columns 和 auto-columns 的 min-content width 被视为零
-
如果单元格的宽度是百分比,则单元格会忽略其边框和内边距尺寸(box-sizing 被忽略)
-
如果当百分比基于可分配表格宽度解析时, 基于此解析的列宽之和会超过可分配表格宽度, 则改为相对于其百分比值进行解析,使得 列宽之和恰好等于可分配表格宽度。
-
尺寸计算为百分比与像素长度之和的列,必须像它们 被计为两列那样确定尺寸, 一列具有像素值,另一列具有百分比值。这不同于将百分比直接解析掉, 因为基于百分比的列的宽度分配方式有所不同。
3.9.3.2. 将多余宽度分配给列
将多余宽度分配给列的规则可以通过 两种方式调用:
- 用于在计算列的使用宽度时, 将表格的多余宽度分配给其列(用于使用宽度计算),或者
- 用于将跨越多列的单元格的多余 max-content 或 min-content 宽度 分配给其所跨越列的 max-content 或 min-content 宽度(用于固有宽度计算)。
这两种情况的规则大体相同,但存在细微差异。
本节剩余部分使用术语 已分配宽度来指代正在被分配的这些宽度之一, 而 多余宽度 用来指代正在分配的宽度 超过其要分配到的列的已分配宽度之和的量。
- 如果存在非受约束列,这些列具有 intrinsic percentage width 为 0% 且 max-content width 非零的起始单元格(也就是本规则允许增长的列), 则本规则允许增长的列的已分配 宽度 会按 max-content width 的比例增加, 使总增加量等于多余宽度。
- 否则,如果存在非受约束列,这些列具有 intrinsic percentage width 为 0% 的起始单元格(也就是本规则允许增长的列, 由于前一条规则,它们必须具有零 max-content width), 则本规则允许增长的列的已分配 宽度 会按相等的量增加, 使总增加量等于多余宽度。
- 否则,如果存在 intrinsic percentage width 为 0% 且 max-content width 非零的受约束列(也就是本规则允许增长的列,由于 其他规则,它们必须具有起始单元格), 则本规则允许增长的列的已分配 宽度 会按 max-content width 的比例增加, 使总增加量等于多余宽度。
- 否则,如果存在 intrinsic percentage width 大于 0% 的列(也就是本规则允许增长的列,由于 其他规则,它们必须具有起始单元格), 则本规则允许增长的列的已分配 宽度 会按 intrinsic percentage width 的比例增加, 使总增加量等于多余宽度。
- 否则,如果存在任何这样的列, 则所有具有起始单元格的列的已分配 宽度 会按相等的量增加, 使总增加量等于多余宽度。
- 否则, 所有列的已分配 宽度 会按相等的量增加, 使总增加量等于多余宽度。
- 如果存在任何未指定宽度的列,则多余宽度会等量分配给这些列
- 否则,如果存在来自基础分配的非零长度宽度的列,则多余 宽度按这些列的宽度比例分配
- 否则,如果存在来自基础分配的非零百分比宽度的列,则多余 宽度按这些列的百分比宽度比例分配
- 否则,多余宽度会等量分配给零尺寸列
测试
3.10. 可用高度分配
测试
3.10.1. 计算表格高度
表格的高度是各行高度之和加上任何单元格间距或边框。 如果表格具有一个值不是 auto 的 height 属性,则它被视为 table grid 的最小高度, 如果各行的整体最小 高度最终小于这个数值,则它最终会分配给各行的高度。 如果它们的整体尺寸最终大于指定的 height,则指定的 height 将没有效果。
行的最小高度是以下各项中的最大值:
- 其对应 table-row(如果有)的计算 height (如果是确定的,百分比被视为 0px)
- 每个仅跨越当前行的单元格的计算 height (如果是确定的,百分比被视为 0px),以及
- 跨越该行的单元格所需的最小高度(ROWMIN)。
ROWMIN 定义为 首次行布局遍历之后各行最小高度的总和。
因此,要计算表格的高度, 有必要对其所有行执行一次首次遍历布局, 计算所有最小行高度加上间距/边框的总和, 并返回该值与 table-root 指定的 height(min-height)中的较大者。
一旦确定表格高度, 行通常会进行第二次布局遍历(此时其单元格高度不再被视为 auto), 然后会发生高度分配,以调整其高度,使其整体满足 表格高度, 随后 table-cell 后代可能会进行第二次布局(其中 它们的百分比高度会被解析)。
测试
- absolute-crash.html (实时测试) (源代码)
- absolute-tables-004.html (实时测试) (源代码)
- absolute-tables-005.html (实时测试) (源代码)
- absolute-tables-007.html (实时测试) (源代码)
- empty-table-height.html (实时测试) (源代码)
- max-height-table.html (实时测试) (源代码)
- min-height-table.html (实时测试) (源代码)
- min-height-table-2.html (实时测试) (源代码)
- min-max-size-table-content-box.html (实时 测试) (源代码)
- table-border-paint-caption-change.html (实时 测试) (源代码)
- percentages-grandchildren-quirks-mode-001.html (实时 测试) (源代码)
- percentages-grandchildren-quirks-mode-002.html (实时 测试) (源代码)
- table-as-item-cell-percentage-001.html (实时 测试) (源代码)
- table-as-item-cell-percentage-002.html (实时 测试) (源代码)
- table-as-item-cell-percentage-003.html (实时 测试) (源代码)
- table-as-item-cell-percentage-004.html (实时 测试) (源代码)
- visibility-collapse-non-rowcol-001.html (实时 测试) (源代码)
- visibility-collapse-row-001.html (实时 测试) (源代码)
- visibility-collapse-row-002-dynamic.html (实时 测试) (源代码)
- visibility-collapse-row-003-dynamic.html (实时 测试) (源代码)
- visibility-collapse-row-004.html (实时 测试) (源代码)
- visibility-collapse-row-005.html (实时 测试) (源代码)
- visibility-collapse-row-group-001.html (实时 测试) (源代码)
- visibility-collapse-row-group-002.html (实时 测试) (源代码)
- visibility-collapse-rowcol-001.html (实时 测试) (源代码)
- visibility-collapse-rowcol-002.html (实时 测试) (源代码)
- visibility-collapse-rowspan-001.html (实时 测试) (源代码)
- visibility-collapse-rowspan-003-border-separate.html (实时 测试) (源代码)
- visibility-collapse-rowspan-003.html (实时 测试) (源代码)
- visibility-hidden-row-001.html (实时 测试) (源代码)
- visibility-hidden-row-002.html (实时 测试) (源代码)
3.10.2. 行布局(第一遍)
行的最小高度(不含与跨越相关的高度分配) 定义为一个假想 linebox 的高度, 该 linebox 包含起始于该行的单元格, 并且跨越多行的单元格被视为具有 0px 高度 (但具有其正确基线)。 在此假想 linebox 中,单元格高度被视为 auto, 其宽度(包括边框和内边距)会被强制为其所跨越列的宽度和内部间距, 但其其他属性会保留。
为了计算此高度, 表格单元格中其高度依赖于父单元格高度百分比的后代(见下节), 如果它们的 overflow 被设为 visible、clip 或 ,或者如果它们是替换元素, 则会被视为具有 auto 高度; 如果不是,则为 0px 高度。测试用例 !!测试用例
单元格的基线 定义为 单元格中第一个流内行盒的基线,或者 单元格中第一个流内 table-row 的基线, 以较先出现者为准。 如果不存在这样的行盒或 table-row, 则基线是单元格盒内容边缘的底部。
测试
以下是在实践中的工作方式:
td { vertical-align: baseline; outline: 3px solid silver; }
img { float: left; clear: left; width: 32px; height: 32px; }
img[title] { float: none; }
<table><tr>
<td>基线</td>
<td>基线<table><tr><td>之后</td></tr></table></td>
<td><table><tr><td>基线</td></tr></table>之后</td>
<td><table align=right><tr><td>之前</td></tr></table><p>基线</p></td>
<td><img src="http://w3.org/favicon.ico"><p>基线</p></td>
<td><img src="http://w3.org/favicon.ico" title="Baseline"/><br/><img src="http://w3.org/favicon.ico" title="After"></td>
<td><img src="http://w3.org/favicon.ico"><img src="http://w3.org/favicon.ico"><!--基线--></td>
</tr></table>
为了寻找基线,具有 滚动机制(见 overflow 属性)的流内盒 必须被视为滚动到其原点位置。
单元格的基线最终可能位于其底部边框下方, 见下例。
此示例中的单元格具有位于其底部边框下方的基线:
div { height: 0; overflow: hidden; }
<table>
<tr>
<td>
<div> 测试 </div>
</td>
</tr>
</table>
每个表格单元格的 vertical-align 属性决定其在行内的对齐方式。 每个单元格的内容都有基线、顶部、中部和底部,行本身也是如此。
在表格单元格的上下文中,vertical-align 的值具有以下含义:
| baseline | 单元格的基线与其跨越的第一行中其他单元格的基线对齐 (见单元格和行基线的定义)。 |
|---|---|
| top | 单元格盒的顶部与其跨越的第一行的顶部对齐。 |
| bottom | 单元格盒的底部与其跨越的最后一行的底部对齐。 |
| middle | 单元格的中心与其跨越的各行的中心对齐。 |
| ... | 其他值不适用于单元格;该单元格改为按基线对齐。 |
所有具有 'vertical-align: baseline' 的单元格中, 单元格盒顶部到基线之间的最大距离 用于设置该行的基线。 如果某行没有任何具有 'vertical-align: baseline' 的单元格, 则该行的基线是该行中最低单元格的底部内容边缘。
table-root 的 基线是其第一行的基线(如果有)。 否则,它是 table-root 的底部内容边缘。
为了避免歧义情况, 单元格的对齐按以下顺序进行:
- 首先定位按基线对齐的单元格。 这将建立该行的基线。
- 接下来定位具有 'vertical-align: top' 的单元格。 此时该行具有顶部,可能具有基线,并具有临时高度, 即从顶部到目前已定位单元格中最低底部的距离。
- 如果剩余单元格(按底部或中部对齐的单元格)中任何一个 具有大于该行当前高度的高度, 则该行高度会通过降低底部增加到这些单元格中的最大值。
- 最后,为剩余单元格分配其位置。
此示例展示前述算法如何创建一行的各种对齐线。
由于在行布局期间,该行中单元格的指定高度被忽略, 且跨越多于一行的单元格尚未被正确确定尺寸, 它们的高度最终需要分配给它们所跨越的行集合。 这是通过运行与列 测量相同的算法完成的, 其中 span=1 值(对于 min-content)会使用以下各项中的最大值进行初始化: 前一行布局所得高度、 对应 table-row 上指定的高度(如果有), 以及仅跨越该行的单元格上指定的最大高度 (算法从在该分配之上考虑 span 为 2 的单元格开始)。
因应用这些步骤而尺寸增加的行 通过降低其底部进行调整。
其位置依赖于任何已更新行底部的单元格 必须在其各自的行中重新正确定位。
此时,小于其所跨越行的整体高度的单元格盒 会获得额外的顶部和/或底部内边距, 使其内容不会垂直移动, 但其顶部/底部边缘会与其所跨越的第一行/最后一行的边缘相接。
测试
- dynamic-table-cell-height.html (实时 测试) (源代码)
- percent-height-overflow-auto-in-restricted-block-size-cell.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-007.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-children-003.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-children-004.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-children-005.html (实时 测试) (源代码)
- percentage-sizing-of-table-cell-children-006.html (实时 测试) (源代码)
3.10.3. 行布局(第二遍)
一旦确定了表格高度,如有必要,将执行第二次行布局遍历, 以通过考虑行/单元格指定 height 中使用的百分比, 为表格行分配正确的最小高度。 除此之外,第一遍行布局的所有指令都适用(见上文)。
然后,如果第二遍之后表格行的新高度之和 不同于填充先前确定的表格高度所需的值, 则应用下文定义的高度分配算法 (要么通过在第一遍最小高度和第二遍最小高度之间取中间尺寸来收缩行, 要么将所有行的高度增加到超过其第二遍最小高度,以填充可用空间; 无论哪种情况,这都不会影响行的基线)。
3.10.4. 核心分配原则
关于高度分配的调查
3.10.5. 分配算法
第一步是为每一行赋予其基础尺寸和参考尺寸。
其基础尺寸是如果表格没有指定高度时 它本会获得的尺寸 (即评估 ROWMIN 时分配给它的尺寸)。
其参考尺寸是以下各项中的最大值
- 其初始基础高度,以及
- 其新的基础高度 (在第二次布局遍历期间评估的高度, 其中 rowgroups/rows/cells 指定高度中使用的百分比 会根据表格高度解析, 而不是被忽略为 0px)。
第二步是基于这些尺寸计算每一行的最终高度。
如果表格高度等于或小于参考尺寸之和, 则分配给每一行的最终高度将是 基础尺寸和参考尺寸的加权平均值, 该平均值会产生正确的总高度。
否则,如果表格拥有任何“auto-height”行 (即尺寸仅由其内容尺寸决定,且没有任何指定高度的行), 则每个非 auto-height 行获得其参考高度, 而 auto-height 行获得其参考尺寸加上某个增量, 该增量等于达到指定表格高度所缺少的高度 除以这种行的数量。
否则,所有行都获得其参考尺寸加上某个增量, 该增量等于达到指定表格高度所缺少的高度 除以行数。
其位置依赖于任何已更新行底部的单元格 必须在其各自的行中重新正确定位。
此时,小于其所跨越行的整体高度的单元格盒 会获得额外的顶部和/或底部内边距, 使其内容不会垂直移动, 但其顶部/底部边缘会与其所跨越的第一行/最后一行的边缘相接。
测试
- extra-height-given-to-all-row-groups-001.html (实时 测试) (源代码)
- extra-height-given-to-all-row-groups-002.html (实时 测试) (源代码)
- extra-height-given-to-all-row-groups-003.html (实时 测试) (源代码)
- extra-height-given-to-all-row-groups-004.html (实时 测试) (源代码)
- extra-height-given-to-all-row-groups-005.html (实时 测试) (源代码)
- td-different-subpixel-padding-in-same-row-vertical-rl.html (实时 测试) (源代码)
- td-different-subpixel-padding-in-same-row.html (实时 测试) (源代码)
3.10.6. 表格单元格内容布局(第二遍)
一旦 table-height 分配结束,并且行高度加上间距/边框之和等于 表格高度, 那些包含了后代的 table-cells 的内容必须进行 第二次布局遍历,如下所定义;这些后代的百分比高度此前在第一遍行布局规则中被忽略 或被视为 0px(见上文)。
解析 table-cell 内容中的百分比高度: 一旦表格和行的最终尺寸已确定, 在高度分配结束之后, table-cells 的内容也必须经历第二次布局遍历, 其中,如果适当,基于百分比的高度这次会相对于 其父单元格的使用高度进行解析。
适合解析 table-cell 直接子元素上的 百分比高度,条件是该单元格被认为显式指定了其高度, 或者该子元素是绝对定位的,见 CSS 2。
出于兼容性原因,进一步澄清如下: 如果单元格的计算高度是一个长度,或者 其 table-root 祖先的计算高度是长度或百分比, 则该单元格被认为显式指定了其高度, 无论该百分比是否表现为 auto。
<section style="height: var(--wrapper-height)">
<table style="height: var(--table-height)">
<tr>
<td style="height: var(--table-cell-height)">
<div style="height:100%; background:yellow">A</div>
</td>
<td style="height: var(--other-table-cell-height)">
B<br>C
</td>
</tr>
</table>
</section>
| --table-cell-height | --table-height | 结果 |
|---|---|---|
| <length> | <any> | |
| <any> | <length> | |
| <any> | <percentage> | |
| auto | auto | |
| <percentage> | auto |
注意,--other-table-cell-height 和 --wrapper-height 都不会影响
算法结果。
本规范的先前版本错误地说明,当表格具有百分比高度时,
会考虑 --wrapper-height,
但当某个实现落地后出现了兼容性问题,于是该行为随后被特殊处理。
测试
3.11. 单元格、标题和其他表格内部盒的定位
测试
我们需要就 visibility:collapse 的作用达成决议。[Issue #478]
一旦表格网格中每列的宽度和每行的高度都已确定, 该算法的最后一步就是为每个 table-internal 盒分配其最终位置。
下面计算出的 width/height/left/top 定义了 CSS 布局盒的尺寸, 这意味着它们可通过 CSSOM-VIEW 中定义的 offset* 属性访问 (目前仅限于可获得对应 HTMLElement 引用的 css 盒)。
然后调整 table-wrapper 盒 的尺寸,使其包含所有 table-non-root 盒的 margin box, 以及 table-root 的 border-box。
表格内任何 caption-side 为 "top" 的 table-caption 的位置,定义为满足以下条件的矩形:
-
width/height 为:
- 布局期间分配给该标题的 width/height
-
top 位置为以下各项之和:
- 为先前顶部标题预留的高度(包括外边距),如果有
- 与前一个标题进行外边距折叠后剩余的任何必要额外顶部外边距,如果有。
-
left 位置为以下各项之和:
- 该标题的左外边距
- (表格宽度减去标题宽度及其总水平外边距)的一半。
表格内任何 table-cell、table-track 或 table-track-group 盒的位置, 定义为满足以下条件的矩形:
-
width/height 为以下各项之和:
- 所有被跨越的可见列/行的宽度/高度
- 水平/垂直 border-spacing 乘以被跨越的可见列/行数量减一
-
left/top 位置为以下各项之和:
- 对于 top:为顶部标题预留的高度(包括外边距),如果有
- 表格的 padding-left/padding-top 和 border-left-width/border-top-width
- 所有先前可见列/行的宽度/高度
- 水平/垂直 border-spacing 乘以前面可见 列/行的数量加一
- 某方向上第一个轨道之前或最后一个轨道之后的 border-spacing 不包含在 任何轨道或轨道组的广度中。
- 轨道之间的 border-spacing 不包含在任何轨道的广度中,但会包含在 跨越这两个轨道的轨道组广度中。
表格内任何 caption-side 为 "bottom" 的 table-caption 的位置,定义为满足以下条件的矩形:
-
width/height 为:
- 布局期间分配给该标题的 width/height
-
top 位置为以下各项之和:
- 为顶部标题预留的高度(包括外边距),如果有
- 表格的 padding-top 和 border-top-width
- 所有可见行的高度
- 表格的 padding-bottom 和 border-bottom-width
- 为先前底部标题预留的高度(包括外边距),如果有
- 与前一个底部标题进行外边距折叠后剩余的任何必要额外顶部外边距,如果有。
-
left 位置为以下各项之和:
- 该标题的左外边距
- (表格宽度减去标题宽度及其总水平外边距)的一半。
collapse,则该列或行被认为是一个
可见
轨道。
4. 绝对定位
4.1. 以 table-root 作为包含块
如果绝对定位元素的包含 块由 table-wrapper 盒生成, 则该包含块对应于表格外边距所应用到的区域, 包括绘制表格边框的区域以及任何 table-caption 的外边距区域。 偏移属性(top/right/bottom/left) 随后像通常一样,表示从此 containing block 的相应边缘向内的偏移。
绝对定位发生在 table 及其流内内容布局之后, 且不会对任何表格网格轨道的尺寸作出贡献, 也不会以任何方式影响表格网格的大小/配置。
黄色区域是表格内容边缘,黄色箭头表示表格外边距。
绿色区域是表格标题,绿色箭头表示标题外边距。
蓝色区域是表格背景区域, 深蓝色区域是表格边框区域。
黑色区域是相对于表格定位的后代, 箭头表示 top/left/bottom/right 位移。
测试
4.2. 以 table-internal 作为包含块
如果绝对定位元素的包含 块由 table-internal 生成, 则该包含块对应于从布局期间会分配给该盒的区域的左上角开始的区域, 但其尺寸会计算为:如果所有轨道都被视为可见(无论某些盒上是否将 visibility 设为 collapse),布局期间会分配给该盒的 区域所具有的尺寸, 并按适用情况不包括边框和内边距。
偏移属性(top/right/bottom/left) 随后像通常一样,表示从此包含块的相应边缘向内的偏移。
这仅在 Firefox 中有效。不过,它会让将来实现 position:sticky 更容易。[Chrome bug] [Interop risk: Firefox bug] [Issue #858]
测试
4.3. 以 table-internal 盒作为非包含块父级
绝对定位盒的非包含块父级唯一影响,是在 top+bottom 和/或 left+right 最终均为 auto 时, 定义其静态位置。
对于 table-cells,绝对定位内容像通常一样按块布局规则定位。
由于表格修正, 不可能创建一个绝对定位盒, 使其成为非 table-cell 的 table-internal 盒的子级 (更多细节见关于 float 和 position 的注释)。
5. 渲染
5.1. 单元格绘制顺序
表格单元格像通常一样按 DOM 顺序绘制在 table-root 中, 与单元格最终实际绘制的位置无关。
5.2. 空单元格渲染(边框分离模式)
| 名称: | empty-cells |
|---|---|
| 值: | show | hide |
| 初始值: | show |
| 适用于: | table-cell 盒 |
| 继承: | 是 |
| 百分比: | 不适用 |
| 计算值: | 指定的关键字 |
| 规范顺序: | 按语法 |
| 动画类型: | 离散 |
测试
在 边框合并模式中, 此属性没有效果。
在
边框分离模式中,
当此属性的值为 hide 时,
不会在空单元格周围/背后绘制边框或背景。
空单元格是一个 table-cell,它既不包含:
- 浮动内容,也不包含
- 流内内容(不包括已由 white-space 属性处理折叠掉的空白)。
我们能否简化 empty-cells:hide?[Issue #605]
例如,考虑以下标记和 css:
< table > < td >< span ></ span ></ td > < td ></ td > < td >< span ></ span ></ td > </ table >
table{ width : 500 px ; height : 300 px ; empty-cells : hide; } table{ background : black; border : 10 px solid black; } td{ background : white; } table{ border-spacing : 0 px ; } td{ padding : 0 ; }
此代码片段的正确渲染如下所示:
5.3. 绘制背景和边框
5.3.1. 绘制表格背景和边框
与其他盒类型不同,table 和 inline-table 盒 不会围绕其整个 client rect 绘制背景和边框。 实际上,表格标题会在视觉上定位在表格外边距和其边框之间, 这意味着需要修改应用于 table-root 的各种效果的绘制区域。
绘制区域:
- 相对于 table-root 的 content-box 绘制的背景、边框和轮廓, 会相对于由表格网格及其边框间距所占据的矩形区域绘制。
- 相对于 table-root 的 padding-box 绘制的背景、边框和轮廓, 会相对于由表格网格及其边框间距所占据的矩形区域绘制,并在每一侧 扩展 table-root 的内边距。
- 相对于 table-root 的 border-box 绘制的背景、边框和轮廓, 会相对于由表格网格及其边框间距所占据的矩形区域绘制,并在每一侧 扩展 table-root 的内边距和 border-width。
5.3.1.1. 边框合并模式中的变化
当表格以边框合并模式布局时, 其边框和其 table-cells 的边框的渲染会被修改。 以下规则描述其修改方式。
如果§ 5.3 绘制背景和边框中定义的背景和边框绘制规则 未被覆盖,则仍然适用。
非空 table-root 的边框 在边框合并模式中不会绘制, 除非设置了 border-image 属性。
在后一种情况下,边框会被绘制得好像表格边框是其使用值指定大小的两倍, 且好像该多出的部分渲染在 table-root 的内边距区域内。
即使它们不由表格绘制,表格边框仍然在布局中占据其空间。
单元格会渲染这些共享边框。
测试
5.3.2. 绘制单元格背景
除了其自身的 background 之外,table-cell 盒还会 渲染其所属的 table-track 和 table-track-group 盒的背景。 这实际上不同于简单地继承它们的背景, 因为 background-origin 和 background-size 计算实际上会在分组盒的边界上完成, 而不是在单元格的边界上完成。
为了寻找每个表格单元格的背景, 可以将不同的表格盒理解为处于六个叠加层上。 在某一层中设置的背景,只有当其上方的各层具有透明背景时 才会可见。
- 表格背景由表格渲染, 且不影响单元格背景。
- 单元格绘制的第一个背景是其起始所在 table-column-group 的背景(如果有)。 就背景定位而言, 预期一个列组会占据在行/列网格中单个单元格可能占据的最大区域, 同时起始于该列组且不进入任何不属于该列组的列。
- 单元格绘制的第二个背景是其起始所在 table-column 的背景(如果有)。 就背景定位而言, 预期一个列会占据在行/列网格中单个单元格可能占据的最大区域, 同时起始于该列且不进入任何其他列。
- 单元格绘制的第三个背景是其起始所在 table-row-group 的背景(如果有)。 就背景定位而言, 预期一个行组会占据在行/列网格中单个单元格可能占据的最大区域, 同时起始于该行组且不进入任何不属于该行组的行。
- 单元格绘制的第四个背景是其起始所在 table-row 的背景(如果有)。 就背景定位而言, 预期一个行会占据在行/列网格中单个单元格可能占据的最大区域, 同时起始于该行且不进入任何其他行。
- 单元格绘制的第五个背景是其自身背景。 这是所有背景渲染完毕后显示在最上方的背景。
如上图所示,虽然所有行包含相同数量的单元格,
但并非每个单元格都可能具有指定内容。在边框分离模式中,如果它们的 empty-cells
属性值为 hide,
则这些空单元格完全不会渲染,
就像在它们上指定了 visibility: hidden 一样,
从而让表格背景透出。
测试
5.3.3. 绘制单元格边框
在边框分离模式中,表格单元格的边框像通常一样渲染。
5.3.3.1. 边框合并模式中的变化
table-cell 的边框 在边框合并模式中渲染时, 就好像单元格边框是其使用值指定大小的两倍, 且好像该多出的部分渲染在单元格的外边距区域中, 并附加约束: 对于边框中不位于表格任一边缘的每一侧, 该边框实际上会按其真实使用值定义的 border-box 绘制区域进行裁剪, 除非设置了 border-image 属性。
如果应用前述裁剪行为会导致以非整数设备像素数量裁剪边框, 浏览器可以决定改为通过向上取整裁剪区域的 x 值和 y 值, 将裁剪区域吸附到设备像素。 向上取整这些值可确保在普通书写模式中, 在多个单元格之间获得有争议像素的单元格实际上是最左上方的那个, 按照本规范,它比其他单元格具有更高优先级。见 § 5.1 单元格绘制顺序 和 § 3.7.1.1 合并 边框的冲突解决算法。
5.3.4. 边框样式(边框合并模式)
border-style 的某些值对于边框合并模式中的表格 具有不同于通常情况的含义。 这些定义会覆盖 border-style 值的默认行为。
- hidden
-
与
none相同,但还会抑制任何其他边框(见 § 3.7.1.3 边框样式的优先级)。 - inset
-
与
ridge相同。 - outset
-
与
groove相同。
5.4. visibility: collapse 的渲染
当表格部分设置了 visibility: collapse 时,其渲染方式会根据它是在 table-cell、跨越的 table-cell, 还是在 table-track/table-track-group 上而有所不同。
测试
5.4.1. 渲染 visibility: collapse 的表格单元格
如 CSS 2.2 所述,如果 table-cell 的 visibility 被设为 collapse,则其渲染方式与 设置了 visibility: hidden 相同。当你在包含 table-cell 的 table-row 上设置 visibility:collapse 时,就会发生这种情况。 如果你想隐藏一行,但继续显示其中跨越其他行的单元格, 请在这些单元格上设置 visibility:visible,以防止它们继承其值。
如果 table-cell 跨越多于一个 table-track, 并且这些 table-track 中至少有一个被设为 visibility: collapse, 则将内容裁剪到该 table-cell 的 border-box。这意味着该单元格左上角 (rtl 中为右上角)的内容会继续显示, 无论该单元格所跨越的哪个轨道被折叠。
5.4.2. 渲染 visibility: collapse 的 table-track 或 table-track-group
当 table-track 或 table-track-group 具有 visibility: collapse 时, 给定 table-track 或 table-track-group 内单元格贡献的所有背景、边框或轮廓, 会继续绘制在未被完全折叠的单元格上 (因为它们跨越了多个轨道)。测试
- visibility-collapse-colspan-003.html (实时 测试) (源代码)
- visibility-collapse-colspan-crash.html (实时 测试) (源代码)
- visibility-collapse-rowspan-002-border-separate.html (实时 测试) (源代码)
- visibility-collapse-rowspan-002.html (实时 测试) (源代码)
- visibility-collapse-rowspan-004-dynamic.html (实时 测试) (源代码)
- visibility-collapse-rowspan-005.html (实时 测试) (源代码)
- visibility-collapse-rowspan-crash.html (实时 测试) (源代码)
6. 分片
6.1. 跨 fragmentainer 断开
在对表格进行分片时,用户代理必须尝试 保持表格行不被分片, 如果跨越该行的单元格没有跨越任何后续行,并且 它们的高度至少比 fragmentainer 的高度和宽度都小两倍。 其他行称为 可自由分片。
当表格无法完全放入一个 fragmentainer 中, 至少有一行确实完整放入了该 fragmentainer, 并且第一个无法放入该 fragmentainer 的行不是可自由分片时。 用户代理必须插入一些垂直间隙, 插在位于溢出点之前和溢出点处的行之间, 使这两行最终被分隔到兄弟 fragmentainer 中。 如果分片需要重复页眉和页脚,并且页脚被重复, 那么页脚必须紧跟在该 fragmentainer 中的最后一行之后, 并且垂直间隙必须插入在重复页脚之后。
当没有任何行能完整放入当前 fragmentainer, 或者第一个无法放入 fragmentainer 的行是可自由分片时, 用户代理必须把 fragmentainer 中所有剩余高度分配给 该行的单元格,并在每个单元格中独立地放入尽可能多的内容, 然后断到下一个片段,并从每个单元格上一个片段中停止的位置 开始其内容(顶部边框不得在延续片段中重新绘制)。
当 break-before 或 break-after 应用于 table-row-group 或 table-row 盒时, 用户代理必须插入一些垂直间隙, 插在位于断点之前和之后的行之间, 使这两行最终按该属性值的要求 被分隔到兄弟 fragmentainer 中。 如果分片需要重复页眉和页脚,并且页脚被重复, 那么页脚必须紧跟在该 fragmentainer 中的最后一行之后, 并且垂直间隙必须插入在重复页脚之后。
6.2. 跨页重复页眉
当把文档渲染到分页媒体中时, 用户代理必须在表格跨越的每一页上重复页眉行和 页脚行, 如果该页面是表格的 fragmentainer, 如果页眉/页脚应用了 avoid break-inside, 如果这样做所需的高度小于 页面高度的二分之一 (页眉行最多四分之一,且 页脚行最多四分之一),并且 如果这不会导致某一行在该页面上显示两次。
当页眉行被重复时,用户代理必须 留出空间,并在需要时渲染表格顶部边框。 页脚行和表格底部边框同理。
用户代理可以决定将此行为扩展到更多分片上下文, 例如除跨页之外,还跨列重复页眉/行。 渲染静态文档的用户代理更可能采用此行为, 但规范并不要求这样做。
7. 安全考虑
使用 CSS Tables 不会带来任何需要缓解的安全风险。
8. 隐私考虑
使用 CSS Tables 不会带来任何需要缓解的隐私风险。
9. 正在跟踪的 bug 列表
本节不是规范性的。
10. 附录
10.1. CSS 与 HTML 属性之间的映射
HTML4 的默认样式表展示了其模型如何映射到 css 属性和值:
table{ display : table} thead{ display : table-header-group} tbody{ display : table-row-group} tfoot{ display : table-footer-group} tr{ display : table-row} td, th{ display : table-cell} colgroup{ display : table-column-group} col{ display : table-column} caption{ display : table-caption} table, thead, tbody, tfoot, tr, td, th, colgroup, col, caption{ box-sizing : border-box; } thead, tfoot{ break-inside : avoid} table{ box-sizing : border-box; border-spacing : 2 px ; border-collapse : separate; text-indent : initial; } thead, tbody, tfoot, table > tr{ vertical-align : middle; } tr, td, th{ vertical-align : inherit; } td, th{ padding : 1 px ; } th{ font-weight : bold; } table, td, th{ border-color : gray; } thead, tbody, tfoot, tr{ border-color : inherit; } table[ frame=box i], table[ frame=border i], table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border : 1 px solid inset; } table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]) { border-collapse : collapse; border-style : hidden; } table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]), table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]) >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border-color : black; } table[ border=$border] /* if(parseInt($border) > 0) */ { border : /*(parseInt($border) * 1px)*/ outsetrgb ( 128 , 128 , 128 ); } table[ border=$border] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) /* if(parseInt($border) > 0) */ { border : 1 px insetrgb ( 128 , 128 , 128 ); } table[ rules=all i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; } table[ rules=rows i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; border-left : none; border-right : none; } table[ rules=cols i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; border-top : none; border-bottom : none; } table[ rules=none i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : none; } table[ rules=groups i] >:is ( thead, tbody, tfoot) { border-top-width : 1 px ; border-top-style : solid; border-bottom-width : 1 px ; border-bottom-style : solid; } table[ rules=groups i] > colgroup{ border-left-width : 1 px ; border-left-style : solid; border-right-width : 1 px ; border-right-style : solid; } table[ frame=box i], table[ frame=border i], table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-style : outset; } table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-top-style : hidden; } table[ frame=above i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-bottom-style : hidden; } table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=rhs i] { border-left-style : hidden; } table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=rhs i] { border-right-style : hidden; } table[ cellpadding=$x] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) /* if(parseInt($x)>0) */ { padding : /*(parseInt($x) * 1px)*/ ; } table[ cellspacing=$x] /* if(parseInt($x)>0) */ { border-spacing : /*(parseInt($x) * 1px)*/ ; } table[ width=$w] /* if(parseInt($w) > 0) */ { width : /*(parseInt($w) * 1px)*/ ; } table[ width=$w] /* if($w matches /(+|-|)([0-9]+([.][0-9]+|)|([.][0-9]+))[%]/) */ { width : /*(parseInt($w) * 1px)*/ ; } table[ height=$h] /* if(parseInt($h) > 0) { height: /*(parseInt($h) * 1px)*/ ; } table[ height=$h] /* if($h matches /(+|-|)([0-9]+([.][0-9]+|)|([.][0-9]+))[%]/) */ { height : /*(parseInt($h) * 1px)*/ ; } table[ bordercolor=$color] { border-color : /*parseHTMLColor($color)*/ ; } table[ bordercolor] >:is ( tbody, thead, tfoot, tr, colgroup, col), table[ bordercolor] >:is ( tbody, thead, tfoot) > tr, table[ bordercolor] >:is ( tbody, thead, tfoot) > tr >:is ( td, th), table[ bordercolor] > tr >:is ( td, th) table[ bordercolor] > colgroup > col) { border-color : inherit; } table[ bgcolor=$color] { background-color : /*parseHTMLColor($color)*/ ; } table[ align=left i] { float : left; } table[ align=right i] { float : right; } table[ align=center i] { margin-left : auto; margin-right : auto; } caption[ align=bottom i] { caption-side : bottom; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=top i] { vertical-align : top; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=middle i] { vertical-align : middle; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=bottom i] { vertical-align : bottom; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=baseline i] { vertical-align : baseline; } :is ( thead, tbody, tfoot, tr, td, th)[ align=absmiddle i] { text-align : center; } :is ( colgroup, col, thead, tbody, tfoot, tr, td, th)[ hidden] { visibility : collapse; } :is ( td, th)[ nowrap] { white-space : nowrap; } :is ( td, th)[ nowrap][ width=$w] /* if(quirksMode && parseInt($w) > 0) */ { white-space : normal; }
测试
11. (链接 到此处用于缺失章节)
12. 变更
自 2019 年 7 月 27 日 工作草案以来的重要变更:
- 从 [MEDIAQUERIES-5] 引用了术语 分页媒体和 连续媒体。
- 将对 :matches() 伪类的提及替换为 :is()。
- 澄清外部表格元素按照其外部显示类型表现。
- 允许 clip 应用于 table-root 和 table-wrapper 盒。
- 添加了 Web Platform Test 覆盖。