先说一下得出的结论:
获取定位时并不一定会回流
这取决于前面是否有可能导致几何信息变化的操作
一、测试一下下面几种情况
1. 连续修改 - 注意这些修改都是会影响元素尺寸或位置的操作
stopBtn.style.position = 'relative';
stopBtn.style.width = '100px'
stopBtn.style.height = '200px'
stopBtn.style.left = '10px'
stopBtn.style.top = '10px'
结果是一次 recalculate style - layout
2. 改一次查一次
stopBtn.style.position = 'relative';
stopBtn.style.width = '100px'
console.log(stopBtn.offsetWidth);
stopBtn.style.height = '200px'
console.log(stopBtn.offsetHeight);
stopBtn.style.left = '10px'
console.log(stopBtn.offsetLeft);
stopBtn.style.top = '10px' // 一次recalculate style -> layout
console.log(stopBtn.offsetTop);
结果layout了四次
结果是4次layout
3. 写完再一次性读
stopBtn.style.position = 'relative';
stopBtn.style.width = '100px'
stopBtn.style.height = '200px'
stopBtn.style.left = '10px'
stopBtn.style.top = '10px'
console.log(stopBtn.offsetWidth);
console.log(stopBtn.offsetHeight);
console.log(stopBtn.offsetLeft);
console.log(stopBtn.offsetTop);
image.png
结果是一次 recalculate style - layout
4. 同一行先读再写
stopBtn.style.position = 'relative';
stopBtn.style.width = stopBtn.offsetWidth + 100 + 'px';
image.png
结果是 2次
layout
,读 offsetWidth
时会layout
是因为前面有一个修改position
的操作。当我把这行修改
position
的代码删掉时,就只会layout
一次,如下:
image.png
或者当我把修改position
改为修改color
,也只会layout一次:
image.png
5. 增加一次修改
stopBtn.style.position = 'relative';
stopBtn.style.width = stopBtn.offsetWidth + 100 + 'px';
stopBtn.style.height = '300px'
image.png
仍然是2次layout
二、 结论
所以获取定位时是否会回流取决于前面是否有可能导致几何信息变化的操作
如果在读offsetWidth之前改的仅是color,那么不会导致layout:
stopBtn.style.color = 'red';
console.log(stopBtn.offsetWidth);
image.png
三. 验证一下
1. 修改背景色 + getBoundingClientRect
stopBtn.style.backgroundColor = 'blue';
stopBtn.style.backgroundColor = 'blue';
console.log(stopBtn.getBoundingClientRect());
没有Layout
都只有 Recalculate Style
,getBoundingClientRect
时并没有触发 layout
,因为前面改的是背景色
2. 修改背景色 + 文本 + getBoundingClientRect
stopBtn.style.backgroundColor = 'blue';
stopBtn.innerText = 'wxmwxm'; // Layout是由于这里
stopBtn.style.backgroundColor = 'blue';
stopBtn.innerText = 'wxmwxm';
console.log(stopBtn.getBoundingClientRect());
有一次Layout
这两种都是上面的顺序:一次Recalculate Style
和Layout
3. 调换一下顺序:
stopBtn.style.backgroundColor = 'blue';
console.log(stopBtn.getBoundingClientRect());
stopBtn.innerText = 'wxmwxm'; // 这里Recalculate Style (Recalculation Forced) -> Schedule Style Recalculation
// -> Recalculate Style (First Invalidated) ->Layout (First Layout Invalidation)
有2次 Recalculate style 和一次 Layout
4. 加一个改变颜色:
stopBtn.style.backgroundColor = 'blue';
console.log(stopBtn.getBoundingClientRect());
stopBtn.innerText = 'wxmwxm'; // 这里Recalculate Style (Recalculation Forced)
stopBtn.style.color = 'red'; // 这里Schedule Style Recalculation -> Recalculate Style -> Layout
仍然是2次 Recalculate style 和一次 Layout
5. 再加一行 getBoundingClientRect:
stopBtn.style.backgroundColor = 'blue';
console.log(stopBtn.getBoundingClientRect());
stopBtn.innerText = 'wxmwxm'; // 这里 Recalculate style (Recalculation Forced 强制重新计算)
stopBtn.style.color = 'red'; // 这里 Schedule Style Recalculation--计划重新计算
// getBoundingClientRect之前:Recalculate style (Recalculation Forced 、First Invalidated)
// 和 Layout (First Layout Invalidation)
console.log(stopBtn.getBoundingClientRect());
一次layout,因为最后一个getBoundingClientRect之前有一个修改innerText 操作
四. 最后看下可能导致layout的操作(仅举例说明)
注意小红三角的提示
image.png
image.png
image.png