如何在使用CSS表格中的固定宽度单元格时避免“未样式化内容的一闪而过”?

10

我的网页GUI的布局部分由CSS表格驱动。主要是因为我希望“单元格”在任何情况下都具有相同的高度,而不会出现任何与对齐相关的大量麻烦。总体而言,这种方法非常成功。

然而,我遇到了一个问题,即表格中的右侧单元格有时需要一些时间才能呈现,导致左侧单元格暂时占据整个页面宽度。这会产生明显的“闪烁”效果,虽然很小,但有点烦人。所以我决定修复它。

以下是我的页面工作方式的模糊表示:

#tbl      { display: table; width: 200px; border: 1px solid black; }
#tbl-row  { display: table-row; }
#tbl-col1,
#tbl-col2 { display: table-cell; }

#tbl-col1 { width: 50px; background-color: red; }
#tbl-col2 { background-color: blue; }
<div id="tbl">
    <div id="tbl-row">
        <div id="tbl-col1">LHS</div>
        <div id="tbl-col2">RHS</div>
    </div>
</div>

当你使用开发者工具给#tbl-col2添加display: none指令时,一切都很好,[我希望准确地]模拟浏览器呈现引擎在渲染#tbl-col1#tbl-col2之间的时刻。请注意,尽管我已经给#tbl-col1设置了宽度,但是它立即占据了表格的100%宽度。我有点理解为什么会发生这种情况:毕竟,我已要求浏览器使
表现得像表格一样。但是,在这里这是不可取的。我试图通过插入“spacer”来解决这个问题,希望在右侧被渲染之前,它可以扩展以填充右侧所有空间,但是没有设置宽度。

#tbl      { display: table; width: 200px; border: 1px solid black; }
#tbl-row  { display: table-row; }
#tbl-col1,
#tbl-spc,
#tbl-col2 { display: table-cell; }

#tbl-col1 { width: 50px;  background-color: red; }
#tbl-col2 { width: 150px; background-color: blue; }
<div id="tbl">
    <div id="tbl-row">
        <div id="tbl-col1">LHS</div>
        <div id="tbl-spc"></div>
        <div id="tbl-col2">RHS</div>
    </div>
</div>

正如您所看到的,再次隐藏#tbl-col2并没有任何影响:#tbl-col1仍然占据了整个表格的宽度,而不是我允许的50px。
假设我宁愿修复这个问题而不是完全放弃CSS表格布局,我该怎么办?
或者我必须替换布局,甚至更糟糕的是采用JavaScript方法来解决FoUC吗?

它 a) 可能没有帮助,b) 不适用于您的设计模式,但您能否给 #col2 设置 min-width - Jon P
@JonP:似乎没有帮助 :( - Lightness Races in Orbit
这是表格列覆盖整个表格宽度的本质。因此,任何给定的宽度都会阻止表格列覆盖其完整的表格宽度,这是不被接受的。您不能违反全局规则。如果您有两列,则可以保留给定的宽度,但另一列将填补进一步的间隙。您应该调整表格宽度而不是表格列,或者明智地使用display inline-block来构建您的结构。 - Raj
如果你能够放弃使用 display: table;,那么创建列就会变得非常容易,可能类似于这个解决方案 - maioman
4个回答

3

在类似的情况下(例如HTML电子邮件),我想做的是使用空单元格预定义列宽的方式:

.tbl {
    display: table;
    width: 200px;
    border: 1px solid black;
    color: #fff;
}
.tbl-row {
    display: table-row;
}
.tbl-cell {
    display: table-cell;
}
.tbl-col1 {
    width: 50px;
    background-color: red;
}
.tbl-col2 {
    background-color: blue;
}
<h3>Pre-define columns width by adding additional row</h3>

<div class="tbl">
    <div class="tbl-row tbl-format">
        <div class="tbl-cell tbl-col1"></div>
        <div class="tbl-cell tbl-col2"></div>
    </div>
    <div class="tbl-row">
        <div class="tbl-cell tbl-col1">LHS</div>
        <div class="tbl-cell tbl-col2">RHS</div>
    </div>
</div>

如果单元格内没有内容,格式化列就是不可见的,但它仍然告诉浏览器表格应该如何格式化。


这很不错。但出于本问题范围之外的原因(粗略地说,有些页面可能具有不同的列宽/数量,而站点模板的排列方式使得将这些变化“镜像”回头部模板中定义的格式行变得困难),我不确定对我来说是否最有效。但肯定值得记住。 - Lightness Races in Orbit
其实,我认为这是我最喜欢的方法。Oriol的方法更聪明,但这种方法避免了JavaScript、延迟可见性等问题。也许值得修复我在之前评论中描述的问题以获得这个解决方案。 - Lightness Races in Orbit
我本想在这里添加一个250的赏金作为奖励,但是现在显然只允许我添加500的赏金。 - Lightness Races in Orbit

2
这是因为表格默认使用自动表格布局
CSS 2.1规范没有定义该布局模式,但建议一个(非规范性的)算法,它反映了几个流行的HTML用户代理的行为。
根据该算法,

如果使用的宽度大于MIN,则额外的宽度应分配到列上。

然而,它并没有解释应该如何分配。实际上,您尝试插入“间隔”元素的方法在Firefox上可以完美工作,但在Chrome上无法工作。
相反,您可能想尝试固定表格模式,该模式在规范中得到了正确的定义(因此更可靠),通常更快,并且也解决了问题:
在固定表格布局算法中,每列的宽度如下确定:
1. 具有非 "auto" 值的 width 属性的列元素设置该列的宽度。 2. 否则,第一行中具有非 "auto" 值的 width 属性的单元格决定该列的宽度。如果单元格跨越多个列,则将宽度分配到这些列上。 3. 任何剩余的列平均分配剩余的水平表格空间(减去边框或单元格间距)。
根据第三点,当最后一个单元格加载之前,间隔元素将接收剩余的 150px。一旦加载完成,它将接收剩余的 0px。
#tbl { table-layout: fixed; }

.tbl {
  display: table;
  table-layout: fixed;
  width: 200px;
  border: 1px solid black;
}
.tbl-row {
  display: table-row;
}
.tbl-col1,
.tbl-spc,
.tbl-col2 {
  display: table-cell;
}
.tbl-col1 {
  width: 50px;
  background-color: red;
}
.tbl-col2 {
  width: 150px;
  background-color: blue;
}
.hide {
  display: none;
}
While parsing the first cell:
<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-col1">LHS</div>
    <div class="tbl-spc hide"></div>
    <div class="tbl-col2 hide">RHS</div>
  </div>
</div>

While parsing the spacer:
<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-col1">LHS</div>
    <div class="tbl-spc"></div>
    <div class="tbl-col2 hide">RHS</div>
  </div>
</div>

While parsing the second cell:
<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-col1">LHS</div>
    <div class="tbl-spc"></div>
    <div class="tbl-col2">RHS</div>
  </div>
</div>

然而,仍然存在一个问题:直到第一个单元格被完全解析之前,间隔符不会显示。
这意味着必须先加载间隔符,但不能首先显示。不幸的是,CSS表格布局不允许重新排序单元格。但可以通过从HTML中删除非语义间隔符并改用::after伪元素来实现:
#tbl-row:after {
  content: '';
  display: table-cell;
}

.tbl {
  display: table;
  table-layout: fixed;
  width: 200px;
  border: 1px solid black;
}
.tbl-row {
  display: table-row;
}
.tbl-row:after {
  content: '';
  display: table-cell;
}
.tbl-col1,
.tbl-col2 {
  display: table-cell;
}
.tbl-col1 {
  width: 50px;
  background-color: red;
}
.tbl-col2 {
  width: 150px;
  background-color: blue;
}
.hide {
  display: none;
}
While parsing the first cell:
<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-col1">LHS</div>
    <div class="tbl-col2 hide">RHS</div>
  </div>
</div>

While parsing the second cell:
<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-col1">LHS</div>
    <div class="tbl-col2">RHS</div>
  </div>
</div>


嗯,看来我需要添加JavaScript代码来在页面加载完成时移除该类(并使用相同的技术在之前移除'tbl-col2'的宽度)。这仍会在Chrome中出现闪烁;我担心这已经超出了我的问题对于使用"display:none"模拟加载状态的使用范围。 - Lightness Races in Orbit
@LightnessRacesinOrbit 我没有想到在解析第一个单元格时,间隔还不存在。诀窍是使用CSS伪元素。 - Oriol
看起来完成了任务!谢谢 :-) - Lightness Races in Orbit
请注意:列宽将不再根据内容自动调整。请尝试使用“LHSSSSSS”代替“LHS”。 - skobaljic
@skobaljic 由于包装器和每列都有定义的宽度,我认为这种行为是期望的。但是,添加 word-wrap: break-word 可以很好地打破长单词而不是水平溢出。 - Oriol
我也认为这对你可能有好处,只是为了其他访客进行评论。如果您使用固定表格布局,则表格不会扩展。 - skobaljic

1
这个修复方案是否合理?

#tbl      { display: table; width: 200px; border: 1px solid black; }
#tbl-row  { display: flex;  }
#tbl-col1,
#tbl-spc,
#tbl-col2 {
   flex:0;
   overflow:hidden
}

#tbl-col1 { flex-basis: 50px;  background-color: red; }
#tbl-col2 { flex-basis: 150px; background-color: blue; }
<div id="tbl">
    <div id="tbl-row">
        <div id="tbl-col1">LHS-LHS-LHS-LHS</div>
        <div id="tbl-spc"></div>
        <div id="tbl-col2">RHS</div>
    </div>
</div>

编辑:

这里是一个带有前缀和回退的代码片段


是的,这就是我想要的。 :) 谢谢 - Lightness Races in Orbit
据我所见,这仅适用于Chrome,看起来在FF和IE中有问题。使用inline-block不会以这种方式工作(table-cellinline-block之间存在巨大差异,我猜测浏览器会尝试修复它..而Chrome赢得了比赛)。 - skobaljic
@skobaljic:啊,该死的老鼠,你是对的 :( 太美好而不真实了! - Lightness Races in Orbit
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - maioman
@LightnessRacesinOrbit - 这只是一个 CSS hack,即使它能工作,flex 也不是为此而设计的;尽管 Oriol 和 Skobaljic 的答案涉及到标记的更改,但它们同样好;顺便说一句,Skobaljic 创建一个不可见的表头的方法看起来更干净。 - maioman
显示剩余2条评论

0
想试试这个 - JS Fiddle
.tbl{display:table;}
.fixed{width: 200px; border: 1px solid black; }
.tblRow { display: table-row; }
.tblCol{ display: table-cell; }
.col1 .tbl { width: 50px;  background-color: red; }
.col2 .tbl { width: 150px; background-color: blue; display:none; }

正如我在评论中所说,调整表格宽度即可。这里是代码。从样式行 .col2 .tbl { width: 150px; background-color: blue; display:none; } 中删除 display:none,然后你就可以看到所有内容了。这或许是你想要的。

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