动态添加元素时CSS Transition不生效的问题求助
问题:动态添加的消息元素CSS过渡动画不生效
每次调用函数创建消息元素时,希望实现从屏幕顶部滑入的效果,但元素直接出现在目标位置,过渡动画完全没触发。
完整代码示例
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div class="d1" id="d1"> <button class="b1" id="b1">Click here!</button> </div> </body> </html>
JavaScript
const d1 = document.getElementById("d1") const b1 = document.getElementById("b1") let bottomCount = 160 let d3IdCount = 3 let svgIdCount = 4 let messagesCount = 1 let d3ST1 = null let d3ST2 = null let d3ST3 = null let d3AlreadyExists = false function errorMessageUI(message){ const d3 = document.createElement("div") d3.classList.add("d3") d3.id = "d3" const d3Exists = document.querySelector(".d3") if(d3Exists){ messagesCount++ bottomCount += 100 d3IdCount += 1 svgIdCount += 1 d3AlreadyExists = true } else { messagesCount = 1 bottomCount = 160 d3IdCount = 3 svgIdCount = 4 d3AlreadyExists = false } d3.id = `${d3IdCount}` d1.append(d3) d3.innerHTML = `${message} <p class="p3" id="p3"> ${messagesCount > 1? "x" : ''}${messagesCount > 1? messagesCount : ''} </p> <svg style="opacity: 60%" class="svg4" id="svg${svgIdCount}" width="8%" height="54%" viewBox="0 0 16 16"><path class="path4" id="path4" d="m4.12 6.137 1.521-1.52 7 7-1.52 1.52z"/><path class="path4" id="path4" d="m4.12 11.61 7.001-7 1.52 1.52-7 7z"/></svg>` // 原代码:此处setTimeout(0)无法保证触发过渡 d3ST1 = setTimeout(() => { d3.style.transform = `translateY(${bottomCount}%)` }, 0) d3ST2 = setTimeout(() => { d3.style.transform = "translateY(-300%)" clearTimeout(d3ST1) clearTimeout(d3ST2) }, 5000) setTimeout(() => { d3.remove() }, 5201) const crossSVG = document.getElementById(`svg${svgIdCount}`) d3.addEventListener("mouseover", () => { crossSVG.style.opacity = "100%" }) d3.addEventListener("mouseout", () => { crossSVG.style.opacity = "60%" }) crossSVG.addEventListener('click', () =>{ clearTimeout(d3ST1) clearTimeout(d3ST2) setTimeout(() => { d3.remove() }, 201) d3.style.transform = "translateY(-300%)" }) } b1.addEventListener("click", () => { errorMessageUI("hello") })
CSS
.d3{ position: absolute; font-family: sans-serif; transform: translateY(-300%); transition: transform 150ms ease !important; display: flex; background: rgba(239, 87, 87, 0.83); width: 20%; min-height: 5%; text-align: center; border-radius: 5px; border-left: 4px solid #af0e0edb; color: #990e0e; font-size: 1.07rem; justify-content: center; align-items: center; }
解决方案
问题根源
浏览器会合并同一帧内的DOM修改与样式更新操作。你在添加元素到DOM后立刻用setTimeout(0)修改transform,这个操作仍会被浏览器合并,导致初始的translateY(-300%)状态从未被渲染,自然无法触发过渡动画。
修复方法
需要让浏览器先渲染元素的初始状态,再修改样式触发过渡,以下两种方式均可行:
方式1:强制浏览器计算元素样式
在修改transform前,调用window.getComputedStyle(d3).transform,强制浏览器先计算元素的初始样式(即translateY(-300%)),后续修改样式就会被识别为状态变化,触发过渡:
// 替换原setTimeout代码段 window.getComputedStyle(d3).transform; // 强制计算初始样式 d3.style.transform = `translateY(${bottomCount}%)`;
方式2:使用requestAnimationFrame延迟修改
requestAnimationFrame会在浏览器下一次重绘前执行,此时元素已被渲染到DOM中,初始样式生效,修改transform即可触发过渡:
// 替换原setTimeout代码段 requestAnimationFrame(() => { d3.style.transform = `translateY(${bottomCount}%)`; });
额外优化建议
- 无需用
async修饰errorMessageUI函数,函数内无异步操作 - 多个消息元素的定位可改用
top属性代替累加translateY百分比,避免百分比计算的适配问题 - 清理定时器时需确保每个元素的定时器都被正确清除,避免内存泄漏
内容的提问来源于stack exchange,提问作者Arijit Dey
相关产品推荐
相关产品推荐

