Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to interrupt current CSS animation and start another?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

I made some adjustments to your approach:

  1. Instead of assigning both of the animations at the same time, only assign the toastout animation to a certain class, let’s say .out.

  2. When trying to remove a toast, call function removeToast(). It adds class .out to that toast. So you can control when to play the toastout animation, then after the animation is played, remove the element using event animationend.

  3. Each time a new toast is added, try to call removeToast() on every existing toast.

Here’s the full solution:

function createToast() {
  // remove all existing toasts
  [...document.querySelectorAll('.toast')].forEach(removeToast);
  
  const elem = document.createElement("div");
  elem.classList.add("toast");
  elem.innerText = "hello";
  setTimeout(() => removeToast(elem), 3000);
  document.body.appendChild(elem);
}

function removeToast(toast) {
  // if a toast is already removed, ignore
  if (!toast || toast.classList.contains('out')) return;
  
  toast.classList.add('out');
  toast.addEventListener('animationend', () => toast.remove());
}
body {
  background-color: #d7d7d7;
}

.toast {
  display: flex;
  position: fixed;
  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;
  animation-duration: 0.3s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

.toast.out {
  animation-name: toastout;
}

@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>
  <button onclick="createToast()">Add toast</button>
<body>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading