У меня есть следующий пример кода, который создает тосты, которые вылетают из верхней части экрана, остаются на несколько секунд, а затем улетают:
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>Это прекрасно работает, но иногда в моем приложении будет создано другое всплывающее уведомление до того, как текущее исчезнет. В этом случае я хочу, чтобы текущее всплывающее уведомление улетало сразу после создания нового всплывающего уведомления, а не ждало 2,5 секунды.
Для жизни меня, я не могу понять это. Является ли это возможным? Это кажется таким простым, но ничто из того, что я пытаюсь сделать, не дает никакого эффекта.
Сначала я попытался захватить существующий элемент тоста и установить animation-delay на 0s, но это не дало никакого эффекта. Затем я попытался заменить его совершенно новой анимацией, попытался использовать !important и т. д. Во всех случаях тост просто исчезает, а анимация не происходит.
Есть ли способ просто заставить всплывающее уведомление немедленно закончить свою текущую анимацию примерно за 0,4 секунды при создании нового всплывающего уведомления? Чтобы он улетел до того, как столкнется с прилетающим тостом?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам нужно сохранить ссылку на elem и удалить ее при повторном вызове функции. Может быть, что-то вроде этого:
let activeToast;
function clearToast() {
if (!activeToast){
return;
}
activeToast.remove();
activeToast = null;
}
function createToast() {
clearToast();
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
activeToast = elem;
document.body.appendChild(elem);
setTimeout(() => clearToast(), 3000);
}
setInterval(createToast, 3000);
Но если вы хотите отслеживать саму анимацию, вы можете использовать некоторые события анимации.
https://medium.com/front-end-weekly/css-animation-events-in-javascript-d2bfe702636d
отредактировал мой ответ. Я думаю, вы ищете анимационные события
Вы думаете, что это возможно, верно? Возможно, вам нужно добавить переменную, чтобы проверить, отображается ли тост или нет. Если отображается всплывающее уведомление, вы можете сразу же применить elem.remove(), чтобы отобразить только новое всплывающее уведомление. Проверьте ниже, чтобы увидеть, работает ли это для вас.
let isToastDisplayed = false;
function createToast() {
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
if (isToastDisplayed) {
const currentToast = document.querySelector(".toast");
currentToast.remove();
}
isToastDisplayed = true;
setTimeout(() => {
elem.remove();
isToastDisplayed = false;
}, 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>Не уверен, что это прямо отвечает на ваш вопрос, но вы можете выделить анимацию в отдельный класс... тогда с помощью js вы можете проверить существующие и добавить класс!
проверить это
function createToast() {
const existing = document.querySelectorAll('.toast')
if (existing.length > 0) {
existing.forEach(e => {
e.classList.add('toast-out')
})
}
const elem = document.createElement("div");
elem.classList.add("toast");
elem.innerText = "hello";
document.body.appendChild(elem);
setTimeout(() => elem.remove(), 3000);
}
setInterval(createToast, 2000);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;
animation-duration: 0.3s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: forwards;
}
.toast-out {
animation-name: toastout;
animation-duration: 0.5s;
animation-iteration-count: 1;
animation-delay: 0s;
animation-fill-mode: 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>Я внес некоторые коррективы в ваш подход:
Вместо того, чтобы назначать обе анимации одновременно, назначьте анимацию toastout только определенному классу, скажем, .out.
При попытке удалить тост вызовите функцию removeToast(). Это добавляет класс .out к этому тосту. Таким образом, вы можете контролировать, когда воспроизводить анимацию toastout, а затем после воспроизведения анимации удалить элемент с помощью события animationend.
Каждый раз, когда добавляется новый тост, попробуйте вызвать removeToast() на каждом существующем тосте.
Вот полное решение:
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>О, это гениально! Спасибо! Это выглядит великолепно и содержит много хороших идей, которые значительно улучшат качество моего подхода.
О, у меня нет проблем с удалением элемента из DOM. То, что я пытаюсь сделать, это пропустить 2,5-секундную задержку анимации, когда быстро появляется другой тост.