I want to add a class to a div, in an animated way!
const divnew = document.createElement("div");
divnew.classList.add("circle");
const beans = document.getElementById("beans");
beans.append(divnew);
divnew.classList.add("up");
.circle{
background:#FCD299;
border-radius:50%;
width:50px;
height:50px;
position:relative;
top:300px;
left: 500px;
transition:all 300ms;
}
.circle.up{
position:relative;
top:200px;
left: 500px;
}
<div id="beans"></div>
as you can see, it adds a class to a div, and then i want it to add the class up in an animation way. But it doesnt do that. help
The browser first evaluates all the javascript, and afterwards does the CSS stuff (probably glossing over some details, but that's the basic gist). So the browser just sees a div with both the circle and up class. To fix it, you can delay setting the up class, using setTimeout like so:
const divnew = document.createElement("div");
divnew.classList.add("circle");
const beans = document.getElementById("beans");
beans.append(divnew);
setTimeout(() => {
divnew.classList.add("up")
}, 0)
Even if the timeout is set to 0ms, it gets pushed to the microtask queue, which is evaluated later and thus the CSS gets updated and the browser animates the element. So you're getting the animation but it still happens instantly after creating the div.
You need to use requestAnimationFrame in order to allow the element to be rendered without the destination class first, otherwise it doesn't know it needs to transition because the first time it's rendered it has no 'memory' of not having the class.
const div = document.createElement("div");
div.classList.add("circle");
document.body.appendChild(div);
window.requestAnimationFrame(
() => div.classList.add("animate")
);
.circle {
background: #FCD299;
border-radius: 50%;
width: 50px;
height: 50px;
position: absolute;
top: 0px;
left: 0px;
transition: all 3000ms;
}
.circle.animate {
top: calc(100% - 50px);
left: calc(100% - 50px);
}
Alternatively you can instead use a CSS animation and define @keyframes for your desired movement rather than relying on a transition -- this also avoids any need for JavaScript.
.circle {
background: #FCD299;
border-radius: 50%;
width: 50px;
height: 50px;
position: absolute;
animation: 3000ms forwards animate;
}
@keyframes animate {
from {
top: 0;
left: 0;
}
to {
top: calc(100% - 50px);
left: calc(100% - 50px);
}
}
<div class="circle"></div>
(note that the animation-fill-mode is forwards, it will maintain the final value of the animated attributes, rather than just snapping back to unset when the animation finishes)