基于此问题:How do I know the IntersectionObserver scroll direction?
我试图在可观察的回调中重现一些布局/重排案例,但我不能,所以我试图简化用例并最终提出这个问题。
我正在阅读Paul Irish what-forces-layout.md的要点,我的问题很简单。
如果在body元素上没有回调的输入情况肯定会触发布局,请参阅下面的示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text">
<script type="text/javascript">
var elementB = document.querySelector('input');
elementB.focus();
</script>
</body>
</html>
但是如果在单击回调中包装focus
事件,则不会触发布局/重排。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text">
<script type="text/javascript">
var elementB = document.querySelector('input');
function onClick() {
elementB.focus();
}
document.addEventListener('click', onClick);
</script>
</body>
</html>
所以我的问题是为什么不触发布局/重排?
答案 0 :(得分:3)
我不确定如果我的问题是正确的,但在案例1中,当解析器开始执行脚本时,DOMContentLoaded尚未被解雇,它仍在解析文档的其余部分。与此同时,您在focus
上调用 elemB
,您将立即触发布局流程。
在案例2中,除非您单击文档本身,否则根本不会调用onClick
函数。您可以通过打开您提供的小提琴上的“Paint Flashing”来验证这一点。只有单击时输入才会变为绿色。
在第一种情况下,您会在开始时看到输入的短暂闪烁(这是您对 .focus 的调用),然后是整个documentElement(在DOMContentLoaded处)。
如果是两个你只有整个documentElement flash一次(在DOMContentLoaded上,前提是没有其他东西触发reflow / repaint onload事件),然后每次点击只输入一次。
现在据我所知,我已经在我的本地机器上尝试了2个案例,有趣的是在第一个案例中,我在DOMContentLoaded之后看到 2 layout 活动。
但是,如果我从您的案例1中注释掉elementB.focus();
行并再次录制,我会再次看到 2个布局活动。
根据我的理解,浏览器将在启动时执行2个布局操作,一旦它开始解析主体,然后在DOMContentLoaded周围执行一次。 如果通过javascript (通过调用链接中列出的任何方法/属性)执行任何同步强制布局垃圾,浏览器将尝试批量这些操作。< / p>
为了测试这种行为,我修改了你的第一种情况,如下所示:
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" style="position:relative;top:0px;">
<script type="text/javascript">
var elementB = document.querySelector('input');
elementB.focus();
</script>
<script async="true">
setTimeout(function(){
elementB.style.top = parseInt(elementB.style.top) + 5 + "px";
},500)
</script>
</body>
</html>
现在将会发生的事情是,您将在加载事件(~500ms)之后立即进行第三次布局活动(不需要异步)。但如果您要设置setTİmeout 0ms ,您将再次获得 2个布局活动! (如果您看到3个布局,则可能无法保证微任务队列行为,强制同步布局,删除第二个脚本标记内的异步属性和setTimeout换行)。 底线:因此,浏览器对其进行批处理,或者至少这是我在此示例中看到的内容。
对于你的第二种情况,当我以你发布的方式记录它时,我没有看到布局活动(2布局和以前一样)是正确的。但我看到的是每个事件后的一致样式重新计算+更新布局树+绘画。这让我觉得一旦布局树更新,如果不需要布局垃圾,就不会重新计算。为了测试这种行为,我改变了你的第二个脚本,如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" style="position:relative;top:0px;">
<script type="text/javascript">
var elementB = document.querySelector('input');
function onClick() {
elementB.focus();
elementB.style.top = parseInt(elementB.style.top) + 5 + "px";
}
document.addEventListener('click', onClick);
</script>
</body>
</html>
每次单击文档时,输入框将向下移动5个像素。如果您在10秒内记录多次点击事件,您会看到很多更新布局树+重绘AND布局垃圾。这让我觉得如果只是必要的话,在更新布局树之后就会完成布局垃圾。
答案 1 :(得分:1)
这&#39;真的很难说清楚为什么开发工具在这种情况下没有注意到回流,可能是因为布局没有变化,回流算法被短路了,开发工具省略了它。它也可能是别的......
但回流发生了,至少如果必须的话。
要测试重排,IMO最好的方法是触发需要它的东西,而CSS转换也是如此。
var input = document.querySelector('input');
// test our logic without anything that should trigger a reflow
noreflow.onclick = function() {
// from 20px in CSS
test.style.transition = 'none';
test.style.width = '100px'; // should be ignored
test.style.transition = 'width 1s linear';
test.style.width = '20px';
// should go from 20px to 20px => no transition
};
// test with manually triggered reflow
reflow.onclick = function() {
// from 20px in CSS
test.style.transition = 'none';
test.style.width = '100px';
input.focus(); // reflow
// now should be computed as 100px
test.style.transition = 'width 1s linear';
test.style.width = '20px';
// now that should move
}
&#13;
#test {
width: 20px;
height: 20px;
background: red;
}
&#13;
<input type="text"><br>
<button id="noreflow">no reflow</button>
<button id="reflow">reflow</button>
<div id="test"></div>
&#13;