【发布时间】:2017-02-14 14:49:45
【问题描述】:
当我想在继续之前让我的 javascript 执行并允许浏览器应用样式等时,我倾向于使用setTimeout 的常用技术,延迟为0 让回调在队列中排队事件循环结束。但是,我遇到了一种情况,这似乎不能可靠地工作。
在下面的 sn-p 中,我有一个 active 类,它将转换应用于 chaser 元素。
当我将鼠标悬停在目标 div 上时,我想从 chaser 元素中删除 active 类,将 chaser 移动到新位置,然后重新应用 active 类。效果应该是o 应该立即消失,然后淡入它的新位置。相反,opacity 和 top 都应用了过渡,所以o 在不同位置滑动,大部分时间。
如果我将内部超时的延迟增加到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>
【问题讨论】:
-
如果您使用 requestAnimationFrame 而不是 setTimeout(fn,0) 会怎样。如果您执行双重 requestAnimationFrame,那么我认为浏览器应该完成自我更新。
-
@David 可能会起作用,但不确定它是否能提供任何保证,但我会做一些阅读。我更担心
setTimeout没有达到我的预期,因为我过去一直依赖它,现在我想知道还有什么可能被破坏 -
@David 对单个
requestAnimationFrame的初始实验表明它在大多数情况下都有效,但并非总是如此。我猜第二个requestAnimationFrame可能会实现一致性,但我认为这可能只是因为你基本上延迟了足够长的时间来避免竞争条件——我真的很喜欢确定性的东西......
标签: javascript css settimeout