如何在CSS表中使用固定宽度的单元格来避免“Flash of Unstyled Content”?

时间:2021-05-16 08:43:57

My web GUI's layout is partially driven by CSS tables. This is mainly because I want the "cells" to have the same height under all situations without any massive headaches surrounding alignment. Overall, this approach has been very successful.

我的Web GUI布局部分由CSS表驱动。这主要是因为我希望“细胞”在所有情况下都具有相同的高度,而没有任何围绕对齐的大量头痛。总的来说,这种方法非常成功。

However, I do have a problem whereby the right-hand cell in the table can sometimes take a moment to render, causing the left-hand cell to briefly have 100% of the page width. This causes a noticeable "flicker" effect which, although minor, is kind of annoying. And I've decided to fix it.

但是,我确实有一个问题,即表中的右侧单元格有时需要花费一些时间来渲染,导致左侧单元格短暂地具有100%的页面宽度。这会产生明显的“闪烁”效果,虽然很小,但却很烦人。我决定解决它。

Here is a vague representation of how my page works:

这是我的页面如何工作的模糊表示:

#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>

All's well and good until you use your developer tools to give #tbl-col2 a display: none directive, [I hope accurately] simulating the state of the browser's rendering engine in the moments between #tbl-col1 having been rendered, and #tbl-col2 being rendered.

一切都很好,直到你使用你的开发人员工具给#tbl-col2一个display:none指令,[我希望准确]在#tbl-col1渲染之间的时刻模拟浏览器渲染引擎的状态,和#tbl -col2正在渲染。

Notice that #tbl-col1 immediately takes up 100% of the width of the table, despite the width I've given it. I sort of understand why this is happening: after all, I've asked the browser to make the divs behave like tables. Still, it's undesirable here.

请注意,尽管我已经给出了宽度,但#tbl-col1会立即占据表格宽度的100%。我有点理解为什么会发生这种情况:毕竟,我已经要求浏览器让div表现得像表格一样。不过,这在这里是不可取的。

I tried to fix this by inserting a "spacer", hoping that without a width it would expand to fill all the space on the right-hand side until such time as the right-hand side got rendered:

我试图通过插入“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>

As you can see by again hiding #tbl-col2, it made not a blind bit of difference: #tbl-col1 still took the whole width of the table, rather than the 50px I allowed it.

正如你可以通过再次隐藏#tbl-col2看到的那样,它没有一点点差别:#tbl-col1仍然占据了表的整个宽度,而不是我允许的50px。

Assuming I'd rather fix this than abandon the CSS Tables layout altogether, what can I do?

假设我宁愿解决这个问题而不是完全放弃CSS Tables布局,我该怎么办?

Or am I going to have to replace the layout or, even worse, take a JavaScript approach to resolving the FoUC?

或者我将不得不更换布局,或者更糟糕的是,采用JavaScript方法来解决FoUC?

4 个解决方案

#1


3  

What I like to do in similar cases (for example html emails) is to pre-define column widths using empty cells this way:

在类似情况下我喜欢做的事情(例如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>

That formatting column is invisible if there is no content inside cells, but still it tells browser the way table should be formatted.

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

#2


2  

That's because, by default, tables use the automatic table layout.

这是因为默认情况下,表使用自动表格布局。

The CSS 2.1 spec doesn't define that layout mode, but suggests a (non-normative) algorithm, which reflects the behavior of several popular HTML user agents.

CSS 2.1规范没有定义布局模式,但建议使用(非规范)算法,该算法反映了几种流行的HTML用户代理的行为。

According to that algorithm,

根据该算法,

If the used width is greater than MIN, the extra width should be distributed over the columns.

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

However, it doesn't explain how it should be distributed. In fact, your attempt of inserting a "spacer" element works perfectly on Firefox, but not on Chrome.

但是,它没有解释应如何分发。实际上,您插入“spacer”元素的尝试在Firefox上完美运行,但在Chrome上则不然。

Instead, you may want to try the fixed table mode, which is properly defined in the spec (and thus more reliable), is usually faster, and solves the problem too:

相反,您可能想要尝试固定表格模式,该模式在规范中正确定义(因此更可靠),通常更快,并且也解决了问题:

In the fixed table layout algorithm, the width of each column is determined as follows:

在固定表格布局算法中,每列的宽度确定如下:

  1. A column element with a value other than auto for the width property sets the width for that column.
  2. width属性的值为auto的列元素设置该列的宽度。

  3. Otherwise, a cell in the first row with a value other than auto for the width property determines the width for that column. If the cell spans more than one column, the width is divided over the columns.
  4. 否则,第一行中具有除width属性以外的值的单元格将确定该列的宽度。如果单元格跨越多个列,则宽度将在列上分割。

  5. Any remaining columns equally divide the remaining horizontal table space (minus borders or cell spacing).
  6. 任何剩余的列均等地划分剩余的水平表空间(减去边框或单元格间距)。

According to the 3rd point, the spacer element will receive the remaining 150px before the last cell has been loaded. And will receive the remaining 0px once loaded.

根据第3点,间隔元件将在最后一个单元被加载之前接收剩余的150px。并且一旦加载就会收到剩余的0px。

So you need

所以你需要

#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>

However, there is still a problem: the spacer won't be displayed until the first cell has been parsed completely.

但是,仍然存在一个问题:在第一个单元格被完全解析之前,不会显示间隔符。

That means the spacer must be loaded first, but must not be the first to be displayed. Sadly, the CSS table layout does not allow to reorder the cells. But it can be achieved by removing the non-semantic spacer from the HTML and using an ::after pseudo-element instead:

这意味着必须首先加载垫片,但不能首先加载。遗憾的是,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>

#3


1  

would this be a reasonable fix?

这是一个合理的解决方案吗?

#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>

EDIT:

here's a fiddle with prefixes and fallback

这里有一个前缀和后备的小提琴

#4


0  

Wanna try this - JS Fiddle

想试试这个 - JS小提琴

.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; }

As I said in my comment, Play with the table width. So here it is. Remove display:none from style line .col2 .tbl { width: 150px; background-color: blue; display:none; } and all you can see. this is perhaps what you want.

正如我在评论中所说,用表格宽度来播放。所以在这里。从样式行.col2 .tbl中移除display:none {width:150px;背景颜色:蓝色;显示:无;你能看到的一切。这也许就是你想要的。

#1


3  

What I like to do in similar cases (for example html emails) is to pre-define column widths using empty cells this way:

在类似情况下我喜欢做的事情(例如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>

That formatting column is invisible if there is no content inside cells, but still it tells browser the way table should be formatted.

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

#2


2  

That's because, by default, tables use the automatic table layout.

这是因为默认情况下,表使用自动表格布局。

The CSS 2.1 spec doesn't define that layout mode, but suggests a (non-normative) algorithm, which reflects the behavior of several popular HTML user agents.

CSS 2.1规范没有定义布局模式,但建议使用(非规范)算法,该算法反映了几种流行的HTML用户代理的行为。

According to that algorithm,

根据该算法,

If the used width is greater than MIN, the extra width should be distributed over the columns.

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

However, it doesn't explain how it should be distributed. In fact, your attempt of inserting a "spacer" element works perfectly on Firefox, but not on Chrome.

但是,它没有解释应如何分发。实际上,您插入“spacer”元素的尝试在Firefox上完美运行,但在Chrome上则不然。

Instead, you may want to try the fixed table mode, which is properly defined in the spec (and thus more reliable), is usually faster, and solves the problem too:

相反,您可能想要尝试固定表格模式,该模式在规范中正确定义(因此更可靠),通常更快,并且也解决了问题:

In the fixed table layout algorithm, the width of each column is determined as follows:

在固定表格布局算法中,每列的宽度确定如下:

  1. A column element with a value other than auto for the width property sets the width for that column.
  2. width属性的值为auto的列元素设置该列的宽度。

  3. Otherwise, a cell in the first row with a value other than auto for the width property determines the width for that column. If the cell spans more than one column, the width is divided over the columns.
  4. 否则,第一行中具有除width属性以外的值的单元格将确定该列的宽度。如果单元格跨越多个列,则宽度将在列上分割。

  5. Any remaining columns equally divide the remaining horizontal table space (minus borders or cell spacing).
  6. 任何剩余的列均等地划分剩余的水平表空间(减去边框或单元格间距)。

According to the 3rd point, the spacer element will receive the remaining 150px before the last cell has been loaded. And will receive the remaining 0px once loaded.

根据第3点,间隔元件将在最后一个单元被加载之前接收剩余的150px。并且一旦加载就会收到剩余的0px。

So you need

所以你需要

#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>

However, there is still a problem: the spacer won't be displayed until the first cell has been parsed completely.

但是,仍然存在一个问题:在第一个单元格被完全解析之前,不会显示间隔符。

That means the spacer must be loaded first, but must not be the first to be displayed. Sadly, the CSS table layout does not allow to reorder the cells. But it can be achieved by removing the non-semantic spacer from the HTML and using an ::after pseudo-element instead:

这意味着必须首先加载垫片,但不能首先加载。遗憾的是,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>

#3


1  

would this be a reasonable fix?

这是一个合理的解决方案吗?

#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>

EDIT:

here's a fiddle with prefixes and fallback

这里有一个前缀和后备的小提琴

#4


0  

Wanna try this - JS Fiddle

想试试这个 - JS小提琴

.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; }

As I said in my comment, Play with the table width. So here it is. Remove display:none from style line .col2 .tbl { width: 150px; background-color: blue; display:none; } and all you can see. this is perhaps what you want.

正如我在评论中所说,用表格宽度来播放。所以在这里。从样式行.col2 .tbl中移除display:none {width:150px;背景颜色:蓝色;显示:无;你能看到的一切。这也许就是你想要的。