我有几百个这样的"行"元素:
<div class='row'>
<div class='cell'></div>
<div class='cell'></div>
<div class='cell'></div>
</div>
我需要在他们的客户端高度已经呈现在页面上之后。我知道"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());
});
每当 DOM 更改时,Reflow 都会排队,但仅在以下任一情况下执行:
-
没有更多的javascript要处理(脚本结束)或
-
查询必须呈现的计算值(如
clientHeight
.
因此,为避免重排,必须遵循以下规则
-
在更改 DOM 时不要查询它,并且
当您 想要查询 DOM 时,不要更改它
实际上,这意味着在修改完 DOM 后,应在脚本末尾对所有查询操作进行分组。 这样做只会对数千个元素重排一次,而不是对每个元素重排数千次。
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();
});
这是一个有点老的问题,但我在研究这个问题时偶然发现了它,所以我想我会为其他人分享我在研究过程中的发现。
有一个非常好的库可以帮助您避免强制重排,尤其是在较大的应用程序上:https://github.com/wilsonpage/fastdom。
库的想法是将读取和写入分组到不同的阶段。