为什么获取DOM元素某些属性会触发回流重绘?

994 阅读2分钟

首先

我们都知道在修改dom节点的尺寸、显隐等影响浏览器布局从而重新构建Render Tree的过程就是回流,而只改变Render Tree中一些元素的外观风格不影响布局,会引起浏览器的重绘。 先看一段代码:

let container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

分析上面代码,可能会得到结果进行了3次回流,4次重绘,但是结果却是1次回流,1次重绘。这是为什么呢?

浏览器的回流重绘

现代浏览器大多是通过队列机制来批量更新布局,浏览器会将触发回流重绘的操作放在一个队列中,等队列中任务到达一定数量或时间间隔到达一个阈值时清空队列。如果是60HZ刷新率的话,至少要16.6ms为一个阈值。但是当你获取布局信息时,浏览器为了返回准确的测量值,会强制清空队列触发回流重绘,主要包括以下属性和方法:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • width height
  • getComputedStyle()
  • getBoundingClientRect()
  • ...

应用

有如下的代码:

elementA.className = "a-style";
var heightA = elementA.offsetHeight; // layout is needed 
elementB.className = "b-style"; // invalidates the layout 
var heightB = elementB.offsetHeight; // layout is needed again

给元素elementA赋值某个类让其布局发生改变,这意味着浏览器必须回流。但浏览器并不会立即进行回流,它会延迟到下次回流时进行批量处理。但这段javaScript代码会在下次回流之前执行完成,因此在执行到读取offsetHeight时,浏览器必须暂停javaScript代码并重新绘制页面,以便获取准确的数值,所以更好的代码应该是这样的:

elementA.className = "a-style";
elementB.className = "b-style"; // invalidates the layout 
var heightA = elementA.offsetHeight; // layout is needed 
var heightB = elementB.offsetHeight; // layout is needed again

这篇文章就分享到这里了希望对大家有所帮助!

参考

# 为什么读取 DOM 测量值会触发布局/回流?

# 浅谈浏览器的渲染过程,重绘与回流

# 浏览器的回流与重绘