Web Animations
Animating elements with JavaScript — the Web Animations API, requestAnimationFrame, CSS transitions from JS, and GSAP.
transitionend fires once for each property that transitions. Animate transform and opacity together and the listener runs twice. Cleanup code (removing the element, starting the next step, counting completions) runs twice too. Calling remove() twice happens to be harmless, but most follow-up actions aren't.
// <div class="toast">Saved <button class="close">×</button></div>
const toast = document.querySelector(".toast");
let cleanups = 0;
toast.querySelector(".close").addEventListener("click", () => {
toast.style.transition = "transform 0.3s ease, opacity 0.3s ease";
toast.style.transform = "translateX(200px)";
toast.style.opacity = "0";
});
toast.addEventListener("transitionend", (event) => {
cleanups++;
console.log("transitionend:", event.propertyName, "| cleanup runs:", cleanups);
toast.remove();
});
// transitionend: opacity | cleanup runs: 1
// transitionend: transform | cleanup runs: 2Animating elements with JavaScript
element.animate() — the Web Animations API
const element = document.querySelector(".box");
// animate(keyframes, options):
const animation = element.animate(
[
{ transform: "scale(1)", opacity: 1 }, // start
{ transform: "scale(1.5)", opacity: 0.5 }, // middle (optional)
{ transform: "scale(1)", opacity: 1 }, // end
],
{
duration: 600, // milliseconds
easing: "ease", // "linear", "ease", "ease-in", "ease-out", "ease-in-out"
delay: 200, // delay before starting
iterations: 1, // 1 = once, Infinity = loop
fill: "forwards", // "none", "forwards" (keep end state), "backwards", "both"
}
);
// Control playback:
animation.pause();
animation.play();
animation.reverse();
animation.cancel();
animation.finish(); // jump to end
// Events:
animation.addEventListener("finish", () => console.log("Done!"));
animation.addEventListener("cancel", () => console.log("Cancelled"));
// Return value check:
const anims = element.getAnimations(); // all running animations on this elementCSS transitions from JavaScript
// Apply a CSS transition, then change the property:
element.style.transition = "transform 0.3s ease, opacity 0.3s ease";
element.style.transform = "translateX(200px)";
element.style.opacity = "0";
// Listen for transition end:
element.addEventListener("transitionend", (event) => {
console.log("Transition finished:", event.propertyName);
element.remove();
});The example above logs event.propertyName for exactly this reason. When several properties transition together, pick one to react to and ignore the others. ({ once: true } isn't a reliable alternative: it reacts to whichever property finishes first, so it can fire before the others are done when the durations differ.)
const toast = document.querySelector(".toast");
let cleanups = 0;
toast.querySelector(".close").addEventListener("click", () => {
toast.style.transition = "transform 0.3s ease, opacity 0.3s ease";
toast.style.transform = "translateX(200px)";
toast.style.opacity = "0";
});
toast.addEventListener("transitionend", (event) => {
if (event.propertyName !== "opacity") return; // react to ONE property only
cleanups++;
console.log("transitionend:", event.propertyName, "| cleanup runs:", cleanups);
toast.remove();
});
// transitionend: opacity | cleanup runs: 1requestAnimationFrame — frame-by-frame control
// Use for game loops, physics, anything that updates every frame:
let x = 0;
function update(timestamp) {
x = (x + 1) % window.innerWidth;
element.style.transform = `translateX(${x}px)`;
requestAnimationFrame(update); // schedule next frame
}
const frameId = requestAnimationFrame(update);
// Cancel the animation:
// cancelAnimationFrame(frameId);Official Sources
- MDN Web Docs — Web Animations API
- MDN Web Docs — Element.animate()
- MDN Web Docs — Using the Web Animations API
- GSAP Documentation
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.