写了如下代码:(伪)

1
2
<button class="moveBtn">click</button>
<div class="container"></div>
1
2
3
4
5
6
const moveBox = document.querySelector('.container')
const btn = document.querySelector('.moveBtn')
btn.onclick = () => {
moveBox.className = 'container'
moveBox.className = 'container move'
}
1
2
3
4
5
6
7
8
9
.container{
width: 100px;
height: 100px;
background:red;
}
.move{
transition: 3s all ease;
transform: translateY(100px);
}

本来是想实现点击按钮, div重置动画状态, 再次向下移动的

但是发现实际上并没有效果

开始天真的以为浏览器会有对className操作的优化, 所以两个修改className的操作被合并了

打开devtool发现className的确在随着button的点击发生改变

于是我把点击事件触发的函数内的代码改为了:

1
2
3
4
moveBox.className = 'container'
setTimeout(() => {
moveBox.className = 'container move'
}, 0)

发现就可以达到想要的效果了

why ?????

仔细想了想, 大概是跟js的macrotask有一定的关系:

在原来的代码中 macrotask 顺序为: js代码 -> ui render

所以点击button后执行的顺序为: 修改className为’container’ -> 修改className为’container move’ -> ui render

这样其实ui render只有一遍, 所以不会有动画效果

在修改后的代码中 macrotask 顺序为: js代码 -> setTimeout -> ui render -> setTimeout内执行 -> ui render

点击button: 修改className为’container’ -> ui render -> 修改className为’container move’ -> ui render

这样就OK啦