I have the following example code which creates toasts that fly out from the top of the screen, stay for a few seconds, and then fly away:
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 3000);
body {
background-color: #d7d7d7;
}
.toast {
display: flex;
justify-content: center;
align-items: center;
padding: 5px 10px;
max-width: 200px;
transform: translate(0, 0);
background-color: #fff;
border-radius: 50px;
animation-name: toastin, toastout;
animation-duration: 0.3s, 0.5s;
animation-iteration-count: 1, 1;
animation-delay: 0s, 2.5s;
animation-fill-mode: forwards, forwards;
}
@keyframes toastin {
from {
opacity: 0;
transform: translate(0, -40px);
}
to {
opacity: 1;
transform: translate(0, 20px);
}
}
@keyframes toastout {
from {
opacity: 1;
transform: translate(0, 20px);
}
to {
opacity: 0;
transform: translate(50vw, 20px);
}
}
<body><body>
This works fine, but sometimes in my application another toast will be created before the current one is gone. In that case, I want the current toast to fly away immediately upon the creation of the new toast, instead of waiting 2.5 seconds.
For the life of me, I cannot figure this out. Is it possible? It seems so simple, but nothing I try seems to have any effect.
First I tried grabbing the existing toast element and setting the animation-delay
to 0s
, but that had no effect. Then I tried replacing it with an entire new animation, tried using !important
, etc. In all cases the toast just, like, disappears, with no animation happening.
Is there any way to just make the toast immediately finish its current animation in like 0.4 seconds when a new toast is created? So that it flies away before it collides with the incoming toast?
4
Answers
You need to keep a reference to elem and remove it when the function is called again. Maybe something like this:
But if you want to keep track of the animation itself there are some animation events you might want to use.
https://medium.com/front-end-weekly/css-animation-events-in-javascript-d2bfe702636d
You’d think it’s possible, right? Maybe you need to add a variable to check whether there’s a toast being displayed or not. If there’s a toast being displayed, you can apply elem.remove() right away to only show the new toast. Check below to see if that works for you.
Not sure if this directly answer your question but you might be able to separate the animation out on a separate class… then with js you can check for existing ones and add the class out!
check it out
I made some adjustments to your approach:
Instead of assigning both of the animations at the same time, only assign the
toastout
animation to a certain class, let’s say.out
.When trying to remove a toast, call function
removeToast()
. It adds class.out
to that toast. So you can control when to play thetoastout
animation, then after the animation is played, remove the element using eventanimationend
.Each time a new toast is added, try to call
removeToast()
on every existing toast.Here’s the full solution: