Журнал • знания • практикаПоиск по сайту

Сайты, дизайн и самостоятельная работа

Найти материал →

Как настроить обработчик события resize в веб-разработке без лишних вызовов

5 минут чтения

Как настроить обработчик события resize в веб-разработке без лишних вызовов

Чтобы обработчик resize не выполнялся при каждом изменении размера окна, ограничьте частоту вызовов. Используйте debounce для действий после завершения изменения, throttle для периодических обновлений или requestAnimationFrame для вычислений перед отрисовкой. Удаляйте обработчик, когда он больше не нужен, и проверяйте результат при изменении ориентации экрана.

Что важно учесть перед обработкой resize

  • Событие может возникать многократно во время перетаскивания границы окна; не запускайте тяжёлые вычисления без необходимости.
  • Выбирайте способ ограничения частоты по требуемому поведению интерфейса: немедленная реакция, периодическое обновление или действие после паузы.
  • При использовании таймера сохраняйте его идентификатор и отменяйте ожидающий вызов, если обработчик больше не нужен.
  • В компонентах снимайте ровно тот обработчик, который был добавлен: важны совпадающие ссылка на функцию и параметры события.
  • Проверяйте не только изменение размера окна, но и изменение размеров элементов, если именно от них зависит разметка.

Почему resize запускает обработчик чаще, чем нужно

Браузер отправляет события resize по мере изменения размеров окна. Если обработчик при каждом событии пересчитывает макет, измеряет элементы или обновляет состояние приложения, повторная работа может ухудшать отзывчивость страницы.

Ограничение частоты подходит для интерфейсов, где на изменение размера нужно реагировать визуально или пересчитывать данные. Оно не требуется, если обработчик выполняет дешёвую операцию и частые вызовы не создают проблем. Для точной синхронизации с размером отдельного блока рассмотрите ResizeObserver, а не событие окна.

Выберите debounce, throttle или requestAnimationFrame

Для настройки достаточно доступа к коду обработчика и возможности проверить страницу в браузере. Если проект использует компонентный фреймворк, также проверьте его правила создания и очистки побочных эффектов.

Способ Когда применять На что обратить внимание
debounce Нужно выполнить действие после того, как изменения прекратились, например пересчитать сложный макет. Результат появится с задержкой; при непрерывном изменении размера вызов может откладываться.
throttle Нужно периодически обновлять интерфейс во время изменения размера. Обновления происходят с ограниченной частотой, а не обязательно после последнего изменения.
requestAnimationFrame Нужно объединить несколько обновлений и выполнить вычисления перед ближайшей отрисовкой. Это не временная задержка после завершения resize; не планируйте кадр повторно без необходимости.

Такие приёмы полезны при создании сайтов под ключ и разработке веб-приложений, где изменение размеров может затрагивать несколько частей интерфейса. Для веб-разработки на заказ, услуг frontend-разработки и оптимизации производительности сайта выбирайте способ по требуемой реакции, а не только по привычке.

Настройте debounce с учетом задержки интерфейса

Перед изменением кода учтите ограничения: слишком большая задержка делает интерфейс вялым; слишком маленькая может не сократить число вычислений; отмена таймера не останавливает уже начавшуюся асинхронную работу; глобальный слушатель окна важно удалять при завершении страницы или компонента.

  1. Определите, что должно происходить после паузы.

    Используйте debounce, если промежуточные значения размера не нужны. Например, можно отложить пересчёт сложной сетки до момента, когда пользователь закончит менять размер окна.

  2. Создайте обёртку с отменой таймера.

    Каждый новый вызов отменяет предыдущий таймер и запускает новый. Задержку подберите по ощущениям в конкретном интерфейсе, затем проверьте, не кажется ли обновление запоздалым.

    function debounce(fn, delay) {
      let timerId;
    
      function debounced(...args) {
        clearTimeout(timerId);
        timerId = setTimeout(() => fn.apply(this, args), delay);
      }
    
      debounced.cancel = () => clearTimeout(timerId);
      return debounced;
    }
    
    const updateLayout = debounce(() => {
      // Пересчитать интерфейс
    }, 150);
    
    window.addEventListener("resize", updateLayout);
  3. Подключите и очистите обработчик.

    Для обычной страницы удалите слушатель, когда он больше не нужен, и отмените отложенный вызов. В компоненте выполняйте эту очистку при размонтировании.

    function cleanup() {
      window.removeEventListener("resize", updateLayout);
      updateLayout.cancel();
    }
  4. Проверьте задержку на реальном сценарии.

    Меняйте размер окна плавно и затем остановитесь. Убедитесь, что промежуточные пересчёты не выполняются, а итоговое обновление происходит после паузы без заметно неприятной задержки.

Ограничьте частоту вычислений через throttle

Проверьте результат по списку:

  • Обработчик продолжает срабатывать во время изменения размера, но не запускает тяжёлую работу при каждом событии.
  • Интерфейс обновляется достаточно часто для выбранного сценария.
  • После завершения изменения размера отображаются актуальные размеры и положение элементов.
  • Таймер или временная метка учитываются при повторном подключении обработчика.
  • Слушатель окна снимается при завершении работы страницы или компонента.
  • Ошибки в консоли не появляются при быстром изменении размеров и закрытии интерфейса.

Простейший вариант вызывает функцию не чаще заданного интервала:

function throttle(fn, interval) {
  let lastRun = 0;

  return function (...args) {
    const now = Date.now();
    if (now - lastRun < interval) return;

    lastRun = now;
    fn.apply(this, args);
  };
}

Этот пример пропускает промежуточные вызовы и не гарантирует отдельный запуск сразу после последнего события. Если итоговое обновление обязательно, добавьте завершающий вызов или выберите debounce.

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

Распространённые ошибки, из-за которых появляются повторные вызовы или остаются ненужные слушатели:

  • Добавлять слушатель при каждом рендере, не удаляя предыдущую функцию.
  • При очистке передавать в removeEventListener новую анонимную функцию вместо исходной ссылки.
  • Забывать снять слушатель окна при размонтировании компонента.
  • Очищать слушатель, но оставлять запланированный таймер debounce.
  • Создавать новый debounced-обработчик при каждом рендере и терять доступ к прежнему таймеру.
  • Считать, что отмена таймера прерывает уже начатый запрос или асинхронное вычисление.
  • Менять параметры обработчика, например флаг захвата, при удалении слушателя.

В компоненте храните стабильную ссылку на обработчик, подключайте его в подходящем механизме жизненного цикла и возвращайте функцию очистки. В ней удалите слушатель и отмените отложенные действия.

Проверьте поведение при смене ориентации и размера окна

Выберите подход по источнику изменения и требуемой точности:

  • requestAnimationFrame. Уместен, когда нужно объединить обновления и выполнить визуальные вычисления перед отрисовкой. Сохраняйте идентификатор кадра и отменяйте запланированный кадр при очистке.
  • ResizeObserver. Подходит, если нужно реагировать на изменение размера конкретного элемента, а не всего окна. Отключайте наблюдатель, когда он больше не нужен.
  • CSS-медиазапросы. Используйте их, когда меняются только стили в зависимости от ширины экрана и пересчёт на JavaScript не требуется.

Проверьте, что после поворота устройства и изменения размера окна интерфейс получает актуальные размеры, а временная задержка не оставляет страницу в промежуточном состоянии.

Нюансы оптимизации обработчика resize

Как выбрать между debounce и throttle?

Выберите debounce, если действие должно произойти после паузы в изменении размера. Выберите throttle, если интерфейс должен обновляться с ограниченной частотой во время изменения.

Всегда ли requestAnimationFrame заменяет debounce?

Нет. requestAnimationFrame связывает работу с ближайшей отрисовкой, а debounce ждёт паузу в событиях. Выбор зависит от того, нужна ли реакция перед кадром или после завершения изменения размера.

Что будет, если задержка debounce слишком велика?

Обновление интерфейса может заметно отставать от действия пользователя. Уменьшите задержку и проверьте поведение на типичном сценарии использования.

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

Да, если компонент может размонтироваться, слушатель окна следует удалить. Также отмените ожидающий таймер или кадр, созданный обработчиком.

Почему removeEventListener не удаляет слушатель?

Частая причина - передача другой функции вместо той, что использовалась при добавлении. Сохраните ссылку на исходный обработчик и используйте её при удалении.

Когда вместо resize использовать ResizeObserver?

Используйте ResizeObserver, когда важен размер конкретного элемента, который может меняться независимо от окна. Для изменений размеров окна подходит событие resize.

Автор материала

Сергей Макаров

Scroll to Top