Отписка от событий

Библиотека Masonry активно использует событийную модель для уведомления кода приложения о завершении различных внутренних процессов: построения сетки, перерасчёта позиций элементов, добавления новых карточек и других операций.

События позволяют синхронизировать интерфейс с процессом компоновки. Например:

  • запуск анимации после размещения элементов
  • загрузка изображений перед перерасчётом сетки
  • обработка динамически добавленных карточек

Подписка на события выполняется через метод on, а прекращение отслеживания — через метод off.

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


Подписка на события

Перед рассмотрением отписки необходимо понимать механизм регистрации обработчиков.

Синтаксис подписки:

masonryInstance.on(eventName, listener)

Где:

  • eventName — имя события
  • listener — функция-обработчик

Пример:

var grid = document.querySelector('.grid');

var msnry = new Masonry(grid, {
  itemSelector: '.grid-item',
  columnWidth: 200
});

function onLayoutComplete(items) {
  console.log('Layout finished:', items.length);
}

msnry.on('layoutComplete', onLayoutComplete);

После завершения компоновки Masonry вызовет функцию onLayoutComplete.


Основные события Masonry

Наиболее часто используемые события:

layoutComplete

Срабатывает после завершения размещения элементов.

msnry.on('layoutComplete', function(items) {
  console.log('Items positioned:', items);
});

removeComplete

Вызывается после удаления элементов из сетки.

msnry.on('removeComplete', function(items) {
  console.log('Removed items:', items);
});

revealComplete

Срабатывает после появления элементов.

msnry.on('revealComplete', function(items) {
  console.log('Items revealed:', items);
});

hideComplete

Срабатывает после скрытия элементов.

msnry.on('hideComplete', function(items) {
  console.log('Items hidden:', items);
});

Метод off

Для отмены подписки используется метод:

masonryInstance.off(eventName, listener)

Параметры:

  • eventName — имя события
  • listener — функция, ранее переданная в on

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


Базовый пример отписки

function onLayoutComplete(items) {
  console.log('Layout completed');
}

msnry.on('layoutComplete', onLayoutComplete);

// отмена подписки
msnry.off('layoutComplete', onLayoutComplete);

После вызова off обработчик больше не будет вызываться.


Почему требуется та же функция

JavaScript сравнивает функции по ссылке. Если передать новую функцию, даже с тем же кодом, отписка не произойдёт.

Некорректный пример:

msnry.on('layoutComplete', function() {
  console.log('Layout done');
});

msnry.off('layoutComplete', function() {
  console.log('Layout done');
});

Функции различаются по ссылке, поэтому подписка остаётся активной.

Корректная реализация:

function handler() {
  console.log('Layout done');
}

msnry.on('layoutComplete', handler);
msnry.off('layoutComplete', handler);

Отписка внутри обработчика

Иногда требуется выполнить обработчик только один раз. Это можно реализовать вручную.

function handleLayout(items) {
  console.log('First layout complete');

  msnry.off('layoutComplete', handleLayout);
}

msnry.on('layoutComplete', handleLayout);

После первого срабатывания обработчик удаляется.


Отписка при уничтожении компонента

При использовании Masonry в SPA-приложениях (React, Vue, Angular) особенно важно удалять подписки при уничтожении компонента.

Пример структуры:

function initGrid() {
  var grid = document.querySelector('.grid');

  var msnry = new Masonry(grid, {
    itemSelector: '.grid-item'
  });

  function handleLayout() {
    console.log('Layout completed');
  }

  msnry.on('layoutComplete', handleLayout);

  return {
    destroy: function() {
      msnry.off('layoutComplete', handleLayout);
      msnry.destroy();
    }
  };
}

Метод destroy:

  • снимает подписку
  • удаляет экземпляр Masonry
  • освобождает ресурсы

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

Без отписки возможно накопление обработчиков при повторной инициализации.

Проблемный сценарий:

function init() {
  var msnry = new Masonry('.grid');

  msnry.on('layoutComplete', function() {
    console.log('Layout done');
  });
}

Если init() вызывается несколько раз:

Layout done
Layout done
Layout done
Layout done

Количество вызовов увеличивается из-за накопленных подписок.

Правильная архитектура включает хранение ссылки на обработчик.


Управление обработчиками через объект

Удобный подход — хранить обработчики в структуре.

var handlers = {
  layout: function(items) {
    console.log('Layout complete', items);
  },
  remove: function(items) {
    console.log('Items removed', items);
  }
};

msnry.on('layoutComplete', handlers.layout);
msnry.on('removeComplete', handlers.remove);

// позже
msnry.off('layoutComplete', handlers.layout);
msnry.off('removeComplete', handlers.remove);

Такой способ облегчает поддержку кода.


Отписка при динамической смене сетки

В приложениях с динамической сменой контейнеров необходимо отключать старые обработчики перед созданием нового экземпляра Masonry.

var msnry;
var layoutHandler;

function createGrid(container) {
  if (msnry) {
    msnry.off('layoutComplete', layoutHandler);
    msnry.destroy();
  }

  msnry = new Masonry(container, {
    itemSelector: '.grid-item'
  });

  layoutHandler = function(items) {
    console.log('New layout');
  };

  msnry.on('layoutComplete', layoutHandler);
}

Это предотвращает привязку обработчиков к устаревшим экземплярам.


Массовая отписка

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

var listeners = [];

function addListener(event, handler) {
  listeners.push({ event, handler });
  msnry.on(event, handler);
}

function removeAllListeners() {
  listeners.forEach(function(item) {
    msnry.off(item.event, item.handler);
  });

  listeners = [];
}

Использование:

addListener('layoutComplete', handleLayout);
addListener('removeComplete', handleRemove);

// очистка
removeAllListeners();

Практический пример жизненного цикла

Полный цикл работы с событиями:

var grid = document.querySelector('.grid');

var msnry = new Masonry(grid, {
  itemSelector: '.grid-item',
  columnWidth: 200
});

function onLayout(items) {
  console.log('Layout finished', items.length);
}

function onRemove(items) {
  console.log('Removed', items.length);
}

msnry.on('layoutComplete', onLayout);
msnry.on('removeComplete', onRemove);

// позже
msnry.off('layoutComplete', onLayout);
msnry.off('removeComplete', onRemove);

Частые ошибки

Использование анонимных функций

msnry.on('layoutComplete', function() {
  console.log('done');
});

Отписаться от такого обработчика невозможно.


Потеря ссылки на обработчик

function init() {
  var handler = function() {
    console.log('layout');
  };

  msnry.on('layoutComplete', handler);
}

Если переменная handler недоступна вне функции, отписка невозможна.


Повторная подписка

msnry.on('layoutComplete', handler);
msnry.on('layoutComplete', handler);

Обработчик будет вызван дважды.


Архитектурные рекомендации

Эффективная работа с событиями Masonry включает несколько принципов:

1. Хранение ссылок на обработчики

const handlers = {};

2. Централизованная регистрация

function bindEvents() {
  msnry.on('layoutComplete', handlers.layout);
}

3. Централизованная очистка

function unbindEvents() {
  msnry.off('layoutComplete', handlers.layout);
}

4. Очистка перед уничтожением

unbindEvents();
msnry.destroy();

Роль отписки в предотвращении утечек памяти

Если обработчик продолжает существовать после удаления DOM-элементов или уничтожения экземпляра Masonry, могут возникать:

  • утечки памяти
  • обращение к несуществующим элементам
  • ошибки выполнения
  • лишние вычисления

Особенно это критично в:

  • SPA-приложениях
  • динамических галереях
  • бесконечной прокрутке
  • системах с частым обновлением контента

Корректное управление подписками обеспечивает стабильность и предсказуемость поведения интерфейса при работе с Masonry.