避免HTML文档重排

时间:2013-11-06 15:16:56

标签: javascript performance dom reflow

我有几百个“行”元素,如下所示:

<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());
});

3 个答案:

答案 0 :(得分:26)

只要DOM发生变化,Reflow就会排队,但只在以下情况下执行:

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

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

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

    • 在更改DOM时不要查询DOM

    • 要查询时不要更改DOM

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

答案 1 :(得分:6)

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

  1. 弄脏当前布局(设置布局属性,如宽度或高度)
  2. 查询布局属性(例如clientHeight)
  3. 您可以轻松批量调用以设置宽度或高度,并且在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(); 
    });
    

答案 2 :(得分:0)

这是一个古老的问题,但在研究此问题时我偶然发现了这个问题,因此我想与他人分享我在研究中发现的发现。

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

该库的想法是将读写分为不同的阶段。