为什么flex-box适用于div,而不适用于表格?

17

以下简单的代码片段会生成一个网页,它将使用可用的屏幕空间,在顶部有一个标题,底部有一个页脚,主要内容尽可能占用空间(有虚线边框以更容易查看):

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  flex-flow: column;
}

h1, small {
  flex: 0 1 auto;
}

div {
  flex: 1 1 auto;
  border: 1px dotted;
}
<!doctype html>
<html>
  <body>
    <h1>Some Header</h1>
    <div>Some Text</div>
    <small>Some Footer</small>
  </body>
</html>

如果我修改CSS和HTML,改用table替换div,它不会扩展表格以占用可用空间:

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  flex-flow: column;
}

h1, small {
  flex: 0 1 auto;
}

table {
  flex: 1 1 auto;
  border: 1px dotted;
}
<!doctype html>
<html>
  <body>
    <h1>Some Header</h1>
    <table><tr><td>Some Content</td></tr></table>
    <small>Some Footer</small>
  </body>
</html>

为什么第一个版本(使用div)可以工作,但第二个版本(使用table)不行?是否有一种方法可以修复第二个示例,使表格扩展并占用所有可用空间(而不引入滚动条)?
一些说明:我的表将具有一行标题(所有标题具有相同的宽度)和几行(所有行具有相同的宽度/高度)。我知道可以使用一堆
和更多CSS重新创建表格,但走这条路感觉像是弃婴与洗脸水一起扔掉了(而且,如果我只是绕过它来解决问题,我就不会问这个问题学习了)。另外,JavaScript 在这里不可用(而且似乎过于复杂)。
我知道足够的 CSS/HTML 将自己置于麻烦之中,但不足以让自己摆脱困境...
编辑: aavrug 建议使用 display: flextable,这样表格就可以正确地扩展以填充区域,但当我向表格添加多行/列时,它们不再等分。我想保留 table 的单元格等分布。

由于表格具有默认样式 display:table;,只需要使用 display:flex; 覆盖它即可。 - aavrug
2
嗯,这样做会导致我的表格单元格分布不均。 - Cornstalks
我想你也必须将表格行设置为 display:flex,因为 <td> 元素是 flex 父元素的子元素的子元素。 - Jhecht
1
@Cornstalks,那是因为您覆盖了它,使其不呈现为表格。只需在其中放置一个<div>并对其使用flex即可。 - Florian Wendelborn
1
@Dodekeract 这只会导致同样的问题 - 如何使表格在 flex 容器内保持 100% 的高度? - Jon Uleis
@Dodekeract:放置一个div在哪里?作为表格的包装器 (例如 <div><table><tr><td>一些内容</td></tr></table></div>) ?如果是这样,那么div会占据所有的空间但不包括内部的表格。 - Cornstalks
4个回答

10

我认为问题在于表格框(table box)被放置在表格包装框(table wrapper box)内:(详见链接)

表格生成一个主块级盒子,称为表格包装盒子 (table wrapper box),其中包含表格框本身和任何标题框

enter image description here

因此,表格框不再是弹性容器的子元素,因此也不是弹性项目。弹性项目是表格包装盒子,但您却将flex属性设置在了table元素上,并且:

不可继承属性的值作用于表格框而非表格包装框

因此,您的flex作用于一个不是弹性项目的元素上,所以被忽略了。

如果该属性应用于表格包装框,则可能有效,但无法选择它。即使能够选择,也不清楚它是否应根据其生成的表格布局进行大小调整,还是参与的弹性布局。

解决方案很简单:

  1. 将表格放入一个包装器中,该包装器将成为弹性项目
  2. 使用弹性布局调整该弹性项目的大小
  3. 将表格取出流并相对于弹性项目给它确定的长度

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
body {
  display: flex;
  flex-flow: column;
}
h1, small {
  flex: 0 1 auto;
}
div {
  position: relative;
  flex: 1 1 0; /* Chrome needs non-auto flex-basis */
  overflow: auto;
}
table {
  position: absolute;
  height: 100%;
  width: 100%;
  left: 0;
  top: 0;
  table-layout: fixed;
  border-collapse: collapse;
}
td {
  border: 1px dotted;
  text-align: center;
}
<h1>Some Header</h1>
<div>
  <table><tr>
    <td>This</td>
    <td>is</td>
    <td>equidistributed.</td>
  </tr><tr>
    <td>This</td>
    <td>is also</td>
    <td>equidistributed.</td>
  </tr></table>
</div>
<small>Some Footer</small>

仅供娱乐,一个简单的避免添加包装器的hacky方法是将表格设置为块级元素,并将自动插入的tbody作为表格。

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
body {
  display: flex;
  flex-flow: column;
}
h1, small {
  flex: 0 1 auto;
}
table {
  display: block;
  position: relative;
  flex: 1 1 0;
}
tbody {
  display: table;
  position: absolute;
  height: 100%;
  width: 100%;
  left: 0;
  top: 0;
  table-layout: fixed;
  border-collapse: collapse;
  box-sizing: border-box;
}
td {
  border: 1px dotted;
  text-align: center;
}
<h1>Some Header</h1>
<table><tr>
  <td>This</td>
  <td>is</td>
  <td>equidistributed.</td>
</tr><tr>
  <td>This</td>
  <td>is also</td>
  <td>equidistributed.</td>
</tr></table>
<small>Some Footer</small>


如果所使用的弹性基础为内容或取决于其可用空间,并且弹性容器在最小内容或最大内容约束条件下进行调整大小(例如,在执行自动表格布局[CSS21]时),请将该项大小调整为该约束条件下。弹性基础大小是该项的主要尺寸结果。 - BoltClock
这看起来很有趣,但请注意表格的高度没有扩展到100%。 - Cornstalks
@Cornstalks:哦,又是Chrome和Firefox的flexbox差异!(或者可能是表格...?) - BoltClock
@BoltClock 但是自动表格布局是一种宽度算法,而这种情况是针对高度的,所以我认为它更加复杂。 - Oriol
@AndreiGheorghiu 因为这样 height: 100% 才能生效。否则它将被视为循环定义并被视为 height: auto - Oriol
显示剩余3条评论

2

在 flex 容器中,HTML 的 table 元素保留其 display 属性:

display: table

因此,它不接受弹性属性。然而,只需用 display: flex 覆盖该规则,布局就应该可以正常工作。

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
body {
  display: flex;
  flex-flow: column;
}
h1,
small {
  flex: 0 1 auto;
}
table {
  display: flex;
  flex: 1 1 auto;
}
tbody {
  display: flex;
  width: 100%;
}
tr {
  display: flex;
  width: 100%;
}
td {
  flex: 1;
  border: 1px solid red;
}
<h1>Some Header</h1>
<table>
  <tr>
    <td>Some Content</td>
    <td>Some Content</td>
    <td>Some Content</td>
    <td>Some Content</td>
    <td>Some Content</td>
  </tr>
</table>
<small>Some Footer</small>


2
但是,表格不会像使用“width: 100%; height: 100%;”时那样等分其单元格。 - Cornstalks
3
没有理智的人会像那样将一个表格改成块级盒子。它是一个表格!而且表格有非常具体的布局规则,除非作者特别希望以非表格方式排列它们及其内容,否则应该保留这些规则。 - BoltClock
1
@BoltClock,我的目标是解决为什么表格元素在 flex 容器中无法工作的问题。哲学问题就留给你了。我只是一个简单的人 :-) - Michael Benjamin
@Michael_B:当然,但绝对不建议将表格更改为display:block!这就像建议使用表格进行布局的反向操作 ;) - BoltClock
@BoltClock,如果我建议 table{display:block} tbody{display:table},你会觉得这太粗糙了吗?XD - Oriol
1
@Oriol:我不知道。仔细想想,使用table { display: block } 实际上只会在表格内部创建一个匿名表格框,因此表格布局不会被破坏,但这意味着表格的任何属性都不适用于该匿名表格框,因此最终你只会得到一个无法样式化的默认表格框。如果表格只有一个tbody... - BoltClock

1
第二个示例(使用<table>作为flex项)应该在Firefox 87中像第一个示例一样正确拉伸(错误1674302)。

0

首先,你应该使用vw和vh单位来设置高度和宽度。虽然它们不被大多数浏览器支持,但它们非常出色。此外,在处理表格问题时,你没有指定宽度,所以只能得到适合文本的单元格。

如果在表格样式中添加类似"width: 75vw;"这样的内容将有所帮助(你可以使用百分比作为宽度)


2
如果您正在使用flex,则不需要使用width:属性,而应该使用flex: ...,这就是他所做的。 - Jhecht
啊,我明白了。不过,我不明白为什么他不干脆这样做,让 tr 标签的宽度都相同呢。 - KevTLW
相反地,vwvh 在所有现代浏览器中都得到支持。 - user663031

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接