← Назад к вопросам

Как сделать чтобы не было сбоев при большом количестве навешанных обработчиков?

1.7 Middle🔥 201 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Event Delegation и оптимизация обработчиков

Когда на странице много обработчиков событий, браузер может испытывать задержки и падение производительности. Основная проблема — каждый обработчик занимает память и требует обработки при срабатывании события.

Проблема: прямое навешивание обработчиков

Если навесить обработчик на каждый элемент, это создаст множество слушателей:

// Плохо: каждому элементу свой обработчик
const buttons = document.querySelectorAll("button");
buttons.forEach((btn) => {
  btn.addEventListener("click", handleClick); // N обработчиков
});

Решение 1: Event Delegation (делегирование событий)

Используй делегирование — навесь один обработчик на родительский элемент и обработай события через event.target:

// Хорошо: один обработчик для всех элементов
const container = document.getElementById("button-container");
container.addEventListener("click", (event) => {
  if (event.target.tagName === "BUTTON") {
    handleClick(event);
  }
});

Преимущества:

  • Одна функция вместо N
  • Динамические элементы работают без переподписки
  • Меньше памяти и утечек

Решение 2: Debounce и Throttle

Для частых событий (scroll, resize, input) ограничь частоту вызовов:

// Debounce — вызвать ПОСЛЕ прекращения события
function debounce(fn, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}

// Throttle — вызвать НЕ ЧАЩЕ чем раз в delay
function throttle(fn, delay) {
  let lastCall = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastCall >= delay) {
      fn(...args);
      lastCall = now;
    }
  };
}

const handleScroll = throttle(() => {
  console.log("Scroll обработан");
}, 100);

window.addEventListener("scroll", handleScroll);

Решение 3: Passive слушатели

Для событий, которые не вызывают preventDefault():

window.addEventListener("scroll", handleScroll, { passive: true });
// passive: true позволяет браузеру оптимизировать прокрутку

Решение 4: Удаление неиспользуемых слушателей

Очищай обработчики при размонтировании компонентов:

useEffect(() => {
  const handler = () => { /* ... */ };
  element.addEventListener("resize", handler);
  return () => element.removeEventListener("resize", handler);
}, []);

React-практики

В React используй встроенную систему событий:

function MyComponent() {
  return (
    <button onClick={() => console.log("Click")}>
      Кнопка
    </button>
  );
}

React автоматически делегирует события на корневой элемент.

Итого

Для больших списков используй:

  1. Event delegation — один обработчик на контейнер
  2. Debounce/Throttle — ограничение частоты для scroll/resize
  3. Passive listeners — для scroll-событий
  4. Cleanup — удаление слушателей при размонтировании

Это даёт бесперебойную работу даже с тысячами элементов.