必要的概念说明

掌握表格相关的名词释义,才能理解表格的形成。在设计中我们需要时刻谨记,表格的宽度是不定的,内容宽度也是不定的,浏览器最终渲染出来的真实列宽,是多个属性共同作用下的结果。

表格宽度 (tableWidth):

table 的 width 属性。可以是自动,也可以是某个百分比或特定的值。自动宽度时,表格宽度会根据内容来改变。以下是自动宽度的三种情况:

  • auto 或不设置时,表格宽度会撑满容器(默认值);
  • min-content 时,表格宽度是由最小单元格内容宽度的列组成;
  • max-content 时,表格宽度是由最大单元格内容宽度的列组成;

image.png
当设置了自动宽度的表格,宽度到达父容器的宽度,效果就跟固定表格宽度一致了。所以自动宽度的表格, 其真实表格宽度可能是又各列撑起的宽度,也可能是父容器的宽度。

定宽列的宽度 (fixedWidth):

某列被设置的宽度,可以理解为我们平时让前端限制某列的列宽多少px,指的就是该项的值。
值得一提的是,定宽列的宽度 (fixedWidth) 会受到两个宽度的影响:列宽度 (colWidth) 和单元格宽度 (cellWidth)。不同的布局方式,有不同的影响。具体在后文会在后文写清楚。

列宽度 (colWidth):

col 标签上设置的 width 属性。也就是通过 Table 组件的 API width 所设置的宽度。

单元格宽度 (cellWidth):

td、th 内设置的 width 属性,也就是单元格里 div 或图片等撑起来的宽度。取较大的值。

真实列宽 (realColWidth):

通过计算后,真实展现在界面中的列宽度。也就是这篇笔记我们所探讨的内容。

单元格内容宽度 (contentWidth):

单元格里面的内容所占的宽度,单元格内容可以是文本、图片或 div 等组成。如果是文本,单元格内容宽度是这段文本排成一行所占的宽度;如果是图片,则是该图片渲染出来的真实尺寸;如果是 div,则宽度与 div 的宽度一致。

最小单元格内容宽度 (minContentWidth):

内容可以被压缩至最小的宽度。如果内容是一段中文,则该值为一个汉字的宽度;如果是一段英文,则该值为这段英文里,宽度最大的字母所占的空间;如果是 div,则该值为 div 的宽度。
值得一提的是,文本的换行规则,容器的 Padding 等也会影响到该宽度的值,下图会帮助大家理解最小单元格内容宽度
image.png

两种布局方式

table-layout 属性定义了用于布局表格单元格,行和列的算法。该属性可被配置为:

  • table-layout: auto;( 是 Ant Design / Arco Design / TDesign 的 Table 组件,以及 HTML 的默认值)
  • table-layout: fixed; ( 是 Element UI 的 Table 组件默认值)
  • 两种布局方式的不同之处:

    • auto 布局下,表格和列的宽度,会受到内容宽度的影响。比如第一列的文本比第二列的长,那么浏览器就会根据内容长度来分配列宽。
    • fixed 布局下,表格和列的宽度,通过列宽度 (colWidth) 来设置,某一列的宽度仅由该列首行的单元格决定,所以内容不会影响列宽。用该方式布局可以使表格的渲染速度更快,因为它只需要加载第一行的内容,就能渲染出表格的宽度、布局。

    两者之间的区别,也可以透过下图来更清楚地认识。
    image.png
    好了,接下来我们正式进入列宽计算的世界吧~

    auto 布局的列宽计算

    在 table-layout: auto; 的布局下,影响真实列宽的有以下三种宽度:

    • 定宽列的宽度 (fixedWidth)
    • 单元格内容宽度 (contentWidth)
    • 表格宽度 (tableWidth)

      类型1:表格中所有列都设置宽度

      场景A:表格宽度足够放下所有列时:

      此时表格的真实列宽,会根据每列所设定的宽度来按比例分配。

      // 定宽列的宽度 = 计算 [列宽度] 和 [单元格宽度],取两者之间较大的值
      fixedWidth = Math.max(colWidth, cellWidth)// 真实列宽 = 计算 [“定宽列的宽度在所有定宽列的宽度内的比例”与”表格宽度”的积] 和 [最小单元格内容宽度] 取两者之间较大的值
      realColWidth = Math.max(fixedWidth / sum(fixedWidth) * tableWidth, minContentWidth)// 真实表格宽度 = 计算 [表格宽度] 和 [所有真实列宽的和],取两者之间较大的值
      realTableWidth = Math.max(tableWidth, sum(realColWidth))

    场景B:表格宽度不足以放下所有列时:

    真实表格宽度会超出容器,出现横向滚动条(如有配置)。此时列宽和表格宽度的计算公式:

    // 定宽列的宽度 = 计算 [列宽度] 和 [单元格宽度],取两者之间较大的值
    fixedWidth = Math.max(colWidth, cellWidth)// 真实列宽 = 计算 [定宽列的宽度] 和 [最小单元格内容宽度],取两者之间较大的值
    realColWidth = Math.max(fixedWidth, minContentWidth)// 真实表格宽度 = 所有真实列宽的和
    realTableWidth = sum(realColWidth)

    上述两种场景的具体表现, 如下图所示:
    image.png

    类型2:表格中所有列都不设置宽度

    场景A:表格宽度足够放下所有列时:

    根据每列的最大内容宽度,来按比例分配列宽。如果单元格内文本没有触发换行,那么此时列宽和表格宽度的计算公式:

    // 不定宽列的宽度 = 计算该列内所有单元格的 [单元格内容宽度] ,取两者之间较大的值
    autoWidth = Math.max(contentWidth)// 真实列宽 = 计算 [“不定宽列的宽度在所有不定宽列的宽度内的比例”与”表格宽度”的积] 和 [最小单元格内容宽度],取两者之间较大的值
    realColWidth = Math.max(autoWidth / sum(autoWidth) * tableWidth, minContentWidth)// 真实表格宽度 = 计算 [表格宽度] 和 [所有真实列宽的和],取两者之间较大的值
    realTableWidth = Math.max(tableWidth, sum(realColWidth))

    场景B:表格宽度不足以放下所有列时:

    每一列都会被压缩至最小内容宽度,并且真实表格宽度会超出容器,出现横向滚动条(如有配置)。此时列宽和表格宽度的计算公式:

    // 不定宽列的宽度 = 最小单元格内容宽度
    autoWidth = minContentWidth// 真实列宽 = 不定宽列的宽度
    realColWidth = autoWidth// 真实表格宽度 = 所有真实列宽的和
    realTableWidth = sum(realColWidth)

    上述两种场景的具体表现, 如下图所示:
    image.png
    注:如果实际情况是处于场景A和场景B之间,即单元格里的文本因为宽度挤压而出现换行,却又没被挤压到内容最小宽度时。我这还暂时没有发现到一条公式可以计算具体的宽度。换言之,上面的公式只对未出现文本换行,或所有列都被挤压到最小宽度时的表格有效。

    类型3:部分列设置宽度,部分列不设置宽度

    场景A:所有不定宽列的最小单元格内容宽度之和小于或等于减去定宽列之后的表格宽度

    不定宽列会按照单元格内容宽度,按比例分配减去定宽列之后的表格宽度。这意味着:
    不定宽列的真实列宽会被按比例压缩或延长;
    定宽列的真实列宽,则按所设置的宽度来展示。
    此时列宽和表格宽度的计算公式:

    // 定宽列的宽度 = 计算 [列宽度] 和 [单元格宽度],取两者之间较大的值
    fixedWidth = Math.max(colWidth, cellWidth)// 不定宽列的宽度 = 计算该列内所有单元格的 [单元格内容宽度] ,取较大的值
    autoWidth = Math.max(contentWidth)// 定宽列的真实列宽 = 计算 [列宽度] 和 [单元格最小宽度],取两者之间较大的值
    realColFixedWidth = Math.max(fixedWidth, minContentWidth)// 不定宽列的真实列宽 = 计算 [“不定宽列的宽度在所有不定宽列的宽度内的比例”与”减去定宽列的真实列宽之后的表格宽度”的积] 和 [最小单元格内容宽度],取两者之间较大的值
    realColAutoWidth = Math.max(autoWidth / sum(autoWidth) * (tableWidth - sum(realColFixedWidth)), minContentWidth)// 真实表格宽度 = 计算 [表格宽度],以及 [定宽列的真实列宽与不定宽列的真实列宽的和],取两者之间较大的值
    realTableWidth = Math.max(tableWidth, sum(realColFixedWidth) + sum(realColAutoWidth))

    帮助理解:类型3-场景A里,计算不定宽列的真实列宽,可以把它当做类型2-场景A 来思考,只是表格宽度是不包含定宽列的真实列宽。

    场景B:所有不定宽列的最小单元格内容宽度之和大于减去定宽列之后的表格宽度

    这时不定宽的列已经被压缩到无法再压缩,只能压缩定宽的列。这意味着:
    不定宽列的内容会以最小内容宽度来展示;
    定宽列,根据所设定的宽度和总定宽列的宽度,按比例压缩展示。
    此时列宽和表格宽度的计算公式:

    // 定宽列的宽度 = 计算 [列宽度] 和 [单元格宽度],取两者之间较大的值
    fixedWidth = Math.max(colWidth, cellWidth)// 不定宽列的宽度 = 计算该列内所有单元格的 [单元格内容宽度] ,取较大的值
    autoWidth = Math.max(contentWidth)// 定宽列的真实列宽 = 计算 [“定宽列的宽度在所有定宽列的宽度内的比例”与”减去不定宽列的宽度的真实列宽之后的表格宽度”的积] 和 [最小单元格内容宽度],取两者之间较大的值
    realColFixedWidth = Math.max(fixedWidth / sum(fixedWidth) * (tableWidth - sum(realColAutoWidth)), minContentWidth)// 不定宽列的真实列宽 = 最小单元格内容宽度
    realColAutoWidth = minContentWidth// 真实表格宽度 = 计算 [表格宽度],以及 [定宽列的真实列宽与不定宽列的真实列宽的和],取两者之间较大的值
    realTableWidth = Math.max(tableWidth, sum(realColFixedWidth) + sum(realColAutoWidth))

    帮助理解:类型3-场景B里,计算定宽列的真实列宽,可以把它当做类型2-场景B 来思考,只是表格宽度是不包含不定宽列的真实列宽。
    上述两种场景的具体表现, 如下图所示:
    image.png

    fixed 布局的列宽计算

    在 table-layout: fixed 布局下,单元格的内容宽度,不会对表格的真实列宽产生影响。这时影响真实列宽的有以下三种宽度:

    • 列宽度 (colWidth)
    • 单元格宽度 (cellWidth)
    • 表格宽度 (tableWidth)

    细心的网友可能发现了,colWidth 和 cellWidth 之间较大的值,就是 auto 布局里 fixedWidth 值。但在 fixed 模式中,如果某一列同时设置了 colWidth、cellWidth,优先级是 colWidth > cellWidth。
    即在 fixed 布局下,通过 API width 所设置的列宽,是比在 td、th 或 div 里设置的宽度,权重要高,不需要比较两者之间的大小。

    类型1:表格中所有列都设置宽度

    场景A:表格宽度足够放下所有列时:

    此时表格的真实列宽,会根据每列所设定的宽度来按比例分配。计算公式如下:

    // 定宽列的宽度 = 列宽度或单元格宽度(优先取列宽度)
    fixedWidth = colWidth || cellWidth// 真实列宽 = 计算 [“定宽列的宽度在所有定宽列的宽度内的比例”与”表格宽度”的积] 和 [定宽列的宽度]取两者之间较大的值
    realColWidth = Math.max(fixedWidth / sum(fixedWidth) * tableWidth, fixedWidth)// 真实表格宽度 = 表格宽度
    realTableWidth = tableWidth

    场景B:表格宽度不足以放下所有列时:

    此时表格的真实列宽,与每列所设定的宽度一致。真实表格宽度会超出容器,出现横向滚动条(如有配置)。计算公式如下:

    // 定宽列的宽度 = 列宽度或单元格宽度(优先取列宽度)
    fixedWidth = colWidth || cellWidth// 真实列宽 = 定宽列的宽度
    realColWidth = fixedWidth// 真实表格宽度 = 所有定宽列的宽度之和
    realTableWidth = sum(fixedWidth)

    image.png

    类型2:表格中所有列都不设置宽度

    场景A:表格宽度足够放下所有列时:

    所有列会等分表格的宽度。

    场景B:表格宽度不足以放下所有列时:

    所有列会等分表格的宽度,由于列宽不受内容控制,所以单元格内的内容有可能会溢出到单元格以外的位置。
    上述两个场景的计算公式:

    // cols = 列数
    // 不定宽列的宽度 = 表格宽度与列数的商
    autoWidth = tableWidth / cols// 真实列宽 = 不定宽列的宽度
    realColWidth = autoWidth// 真实表格宽度 = 表格宽度
    realTableWidth = tableWidth

    image.png

    类型3:部分列设置宽度,部分列不设置宽度

    场景A:表格宽度足够放下所有定宽的列时:

    定宽的列,真实列宽等于所设定的宽度值,不定宽的列会平分表格剩余宽度。计算公式如下:

    // 定宽列的宽度 = 列宽度或单元格宽度(优先取列宽度)
    fixedWidth = colWidth || cellWidth// 定宽列的真实列宽 = 定宽列的宽度
    realColFixedWidth = fixedWidth// autoCols = 不定宽列的列数
    // 不定宽列的真实列宽 = [减去定宽列之后的表格宽度] 与 [不定宽列的列数] 的商
    realColAutoWidth = (tableWidth - sum(realColFixedWidth)) / autoCols// 真实表格宽度 = 表格宽度
    realTableWidth = tableWidth

    场景B:表格宽度不足以放下所有定宽的列时:

    真实表格宽度会超出容器,出现横向滚动条(如有配置)。超出表格宽度的列,都会游离在表格组件以外。如果该列是定宽列,则保持原有宽度;如果该列是不定宽列,则单元格被挤压至最小内容宽度。计算公式如下:

    // 定宽列的宽度 = 列宽度或单元格宽度(优先取列宽度)
    fixedWidth = colWidth || cellWidth// 定宽列的真实列宽 = 定宽列的宽度
    realColFixedWidth = fixedWidth// 不定宽列的真实列宽 = 0
    realColAutoWidth = 0// 真实表格宽度 = 表格宽度
    realTableWidth = tableWidth

    image.png