7

我有几百个这样的“行”元素:

<div class='row'>
  <div class='cell'></div>
  <div class='cell'></div>
  <div class='cell'></div>
</div>

在页面上呈现它们之后,我需要获取它们的 clientHeight。我知道“clientHeight”属性会强制回流,这会影响我的表现,因为它们太多了。但是 - 它们已经被渲染,我知道它们的大小在渲染时间和我查询它们的高度之间没有变化。

有没有办法告诉浏览器在查询高度时不要重排?此外 - webkit 检查员说:

Layout tree size  5901
Layout scope      Whole document

并且 div 位于绝对定位的祖先中 - 不应该只重排绝对定位的元素吗?

编辑:

所以提供的答案是正确的。我实际上弄脏了布局,因为我有这个循环:

rows.each(function(){
  $(this).css('height', this.clientHeight);
});

将代码更改为此解决了问题:

var heights = rows.map(function(){
  return this.clientHeight;
});
rows.each(function(){
  $(this).css('height', heights.shift());
});
4

3 回答 3

30

每当 DOM 更改时,reflow 都会排队,但仅在以下任一情况下执行:

  1. 没有更多的 javascript 来处理(脚本结束)或

  2. 当您查询必须呈现的计算值时,例如clientHeight.

因此,为避免回流,您必须遵循以下规则

  • 更改 DOM 时不要查询它,并且

  • 当你想查询它时不要改变它

实际上,这意味着在完成 DOM 修改后,您应该在脚本末尾对所有查询操作进行分组。这样做只会为数千个元素重排一次,而不是为每个元素重排数千次。

于 2013-11-06T15:29:01.510 回答
6

clientHeight 属性不会强制回流,至少它本身不会。您需要两件事来触发重排:

  1. 弄脏当前布局(设置布局属性,如宽度或高度)
  2. 查询布局属性(例如 clientHeight)

您可以轻松地批量调用来设置宽度或高度,并且在 JS 上下文放弃控制之前不会发生回流。您可以轻松地批量查询布局属性,如果当前布局不脏,则不会发生重排。

当您连续执行1和2时会出现问题。强制回流是为了用准确的信息满足您的查询。

据我所知,您是正确的,拥有绝对定位的祖先应该限制回流在整个文档中“传播”(绝对定位的祖先的父级不会回流)。绝对定位的祖先中的所有项目仍将重排!

至于您的问题的解决方案,是的。设置一个缓存机制,像这样(伪代码):

forEachDiv(function(div) {
  div.getClientHeight = function(){
    return div.cachedClientHeight = div.cachedClientHeight || div.clientHeight;
  }
});

然后,您可以根据需要使用div.getClientHeight()而不是div.clientHeight多次使用,并且只有第一次使用会触发回流(如果脏)。

显然,按照第一个建议,您也可以人为地查询 clientHeight 以获取一批中的所有 div(= 一次回流),之后您就没有更多问题了。

// cache the client height for all divs now, to avoid reflows later
forEachDiv(function(div) {
  div.getClientHeight(); 
});
于 2013-11-06T15:33:36.437 回答
0

这是一个有点老的问题,但我在研究这个问题时偶然发现了它,所以我想我会分享我在研究过程中发现的东西给别人。

有一个很好的库可以帮助您避免强制回流,尤其是在大型应用程序上:https ://github.com/wilsonpage/fastdom 。

该库的想法是将读取和写入分组到单独的阶段。

于 2018-10-10T06:32:40.023 回答