javascript - setTimeout(fn,0) 在应用样式之前触发

标签 javascript css settimeout

当我想放弃执行我的 javascript 并允许浏览器在我继续之前应用样式等时,我倾向于使用 setTimeout 的常用技术,延迟 0 在事件循环结束时有一个回调排队。但是,我遇到了这样一种情况,它似乎无法可靠地工作。

在下面的代码片段中,我有一个 active 类,它向 chaser 元素应用过渡。

当我将鼠标悬停在目标 div 上时,我想从 chaser 元素中删除 active 类,将 chaser 移动到一个新的位置,然后重新应用 active 类。效果应该是 o 应该立即消失,然后淡入其新位置。相反,opacitytop 都应用了过渡,因此 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/

相关文章:

javascript - 使用过滤器过滤对象

javascript - 如何在表中的javascript中将时间戳转换为日期时间

HTML/CSS - 使用自定义字体

javascript - 在 jQuery 中使用 switch 语句

javascript - 脚本在 html 中有效,但在外部无效

javascript - 无法使用 Web Audio API 获得 5.1/环绕声

python - HTML 不在 django 中提供图像

Node.JS:不保持进程运行的setTimeout

Javascript、Promises 和 setTimeout

javascript - Winjs中的setTimeout