Проблемы с работой setTimeout в JavaScript при повторных вызовах

Неправильная работа уведомления при многократном нажатии

Столкнулся с проблемой при создании системы уведомлений. Когда пользователь нажимает кнопку “Купить”, должно появляться небольшое уведомление вверху страницы с сообщением об успешном выполнении операции.

Вот мой код:

function showNotification(){
  var notification = document.getElementById('notification');
  var initialPos = -40;
  notification.style.top = $j(document).scrollTop() + 'px';
  var currentTop = parseInt(notification.style.top);
  var movement = 0;
  var timer = setInterval(animate, 0.8);
  
  function animate(){
    if(movement == 25)
    {
      clearInterval(timer);

      setTimeout(function(){
        jQuery('#notification').fadeOut().delay(1500);
        jQuery('#notification').css("top", initialPos).show()
      }, 800);
    }
    else
    {
        movement = movement + 3;
        notification.style.top = (currentTop + movement) + 'px';
    }
  }
}
.alert-box
{
  position:absolute;
  top:-40px;
  left:50%;
  transform: translateX(-50%);
  z-index: 999;
  background-color:#2c2c2c;
  padding:0.8em;
  color:white;
}
<button id="buy-button" onclick="showNotification()">Купить товар</button>
<div class="alert-box" id="notification">
    <p>Товар добавлен в корзину</p>
</div>

При одном нажатии все работает нормально, но если быстро нажимать кнопку несколько раз подряд, то уведомление либо вообще не показывается, либо отображается с задержками и глюками. Как это исправить?

Вижу что проблема еще и в том, что анимация запускается с неправильной позиции при повторных кликах. У тебя currentTop берется из style.top, но после fadeOut элемент может оставаться в промежуточном состоянии. Попробуй в начале функции всегда принудительно сбрасывать позицию: notification.style.top = (initialPos + $j(document).scrollTop()) + 'px' перед запуском анимации.

Проблема в том, что у тебя накапливаются интервалы и таймауты при каждом клике. Нужно добавить флаг состояния и очищать предыдущие таймеры. Создай глобальную переменную isAnimating = false, в начале функции проверяй if(isAnimating) return; и устанавливай её в true при старте анимации. В конце всех операций возвращай обратно в false. Также лучше сохранять ссылки на таймеры в переменные, чтобы можно было их корректно очистить при необходимости.