Transitionend事件与事件循环问题:CSS过渡及transitionend不触发原因咨询
问题解答
一、Demo2无过渡效果与transitionend事件的原因
CSS transition触发的核心前提是:浏览器能检测到同一个可过渡属性存在前后两个不同的计算值,且两个值的生效时机之间存在至少一次渲染流水线的提交(重排/重绘)。
Demo2中add("visible")和add("move")是在同一个同步JS任务中连续执行的,浏览器会将这两次DOM样式变更批量合并,等到当前JS任务执行完成后再统一执行样式计算、布局、绘制流程。最终渲染时元素的初始状态就同时包含visible和move两个类的样式,没有「display:block、无位移红块」的初始状态和「位移蓝块」的终态差异,transition不会被触发,自然也不会抛出transitionend事件。
二、Demo1跨浏览器表现差异的原因
Demo1将添加move类的逻辑放到了requestAnimationFrame(rAF)回调中,出现跨浏览器差异的核心原因是不同浏览器对rAF回调的执行时机和渲染队列刷新逻辑的实现有差异:
- Chrome的现有实现中,添加
visible类触发的样式变更会在rAF回调执行前完成布局计算,此时元素的初始状态已经是「可见红块」,rAF回调中添加move类相当于在当前帧的渲染前提交了新的样式变更,浏览器能检测到状态差,因此可以触发过渡。 - Firefox的现有实现中,rAF回调仍处于当前帧的样式变更合并阶段,添加
visible和move的操作会被合并处理,最终效果和Demo2一致,没有过渡。
如果需要做全浏览器兼容,可以嵌套两层rAF,确保添加move类的操作发生在上一帧渲染完成后:
requestAnimationFrame(() => { requestAnimationFrame(() => block.classList.add("move")); });
三、补充Demo跨浏览器差异的原因
你的判断是正确的,该现象确实是alert触发了提前重绘导致的。
alert属于阻塞型API,会暂停JS线程的执行,不同浏览器在处理alert弹出前的逻辑不同:
- 部分Chrome版本会在弹出alert前,强制刷新所有待执行的渲染任务,把添加
visible类后的状态渲染到屏幕上,此时元素已经有了可见红块的初始状态。后续setTimeout属于宏任务,会在alert关闭、当前事件循环清空后执行,此时添加move类就会产生状态差,触发过渡。 - 部分Firefox版本不会在alert弹出前刷新渲染队列,添加
visible和move的样式变更还是会被合并,因此不会触发过渡。
额外说明:即使没有alert,默认情况下setTimeout的回调也会在下一次事件循环执行,通常晚于当前帧的渲染提交,所以大部分场景下用setTimeout包裹添加move类的逻辑也能触发过渡,只是setTimeout的执行时机不固定,精度低于rAF。
内容的提问来源于stack exchange,提问作者MaximPro
相关产品推荐
相关产品推荐

