当我想放弃执行我的 javascript 并允许浏览器在我继续之前应用样式等时,我倾向于使用 setTimeout
的常用技术,延迟 0
在事件循环结束时有一个回调排队。但是,我遇到了这样一种情况,它似乎无法可靠地工作。
在下面的代码片段中,我有一个 active
类,它向 chaser
元素应用过渡。
当我将鼠标悬停在目标 div 上时,我想从 chaser
元素中删除 active
类,将 chaser
移动到一个新的位置,然后重新应用 active
类。效果应该是 o
应该立即消失,然后淡入其新位置。相反,opacity
和 top
都应用了过渡,因此 o
幻灯片 从一个位置到另一个位置,< em>大部分时间。
如果我将内部超时的延迟增加到 10
,它就会开始按照我最初的预期运行。如果我将它设置为 5
,那么它有时会,有时不会。
我原以为任何 setTimeout
都会对我的回调进行排队,直到应用样式更新之后,但这里存在明显的竞争条件。我错过了什么吗?有没有办法保证更新的顺序?
我在 macOS 和 Windows 上使用 Chrome 56,尚未测试其他浏览器。
(我知道我可以通过其他方式实现这一点,例如仅将过渡应用于 opacity
属性 - 请将此视为一个人为的示例,以演示有关订购样式更新的特定问题)。
var targets = document.querySelectorAll('.target');
var chaser = document.querySelector('#chaser');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('mouseenter', function(event) {
chaser.className = '';
setTimeout(function() {
// at this point, I'm expecting no transition
// to be active on the element
chaser.style.top = event.target.offsetTop + "px";
setTimeout(function() {
// at this point, I'm expecting the element to
// have finished moving to its new position
chaser.className = 'active';
}, 0);
}, 0);
});
}
#chaser {
position: absolute;
opacity: 0;
}
#chaser.active {
transition: all 1s;
opacity: 1;
}
.target {
height: 30px;
width: 30px;
margin: 10px;
background: #ddd;
}
<div id="chaser">o</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
<div class="target">x</div>
最佳答案
在执行任何其他操作之前,您需要监听的是 transitionend
事件。您可以阅读 MDN about transitionend
event .顺便说一句,永远不要使用 setTimeout
来保证时间。
编辑:这是在 OP 澄清后供引用。每当元素发生样式更改时,都会发生回流和/或重绘。你可以read more about them here .如果第二次 setTimeout 在第一次回流之前运行,那么您将获得滑动效果。之所以10ms会达到预期的效果,是因为offsetTop
属性调整后添加了.active
类(导致transition
属性在元素更改为 offsetTop
后应用)。通常,有 60fps(即:每帧约 16 毫秒),这意味着在应用新样式之前,您有一个 16 毫秒的窗口来做任何事情。这就是为什么 5 毫秒的小延迟有时会导致不同的结果。
TL:DR - 浏览器每 16 毫秒询问 JS 和 CSS 是否有任何更新,并计算要绘制的内容。如果您错过了 16 毫秒的窗口,您可能会得到完全不同的结果。
关于javascript - setTimeout(fn,0) 在应用样式之前触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42229156/