Управление состоянием фильтров

Masonry — это библиотека для создания сеточных макетов с «кирпичной» структурой. Управление состоянием фильтров в ней строится вокруг динамического показа и скрытия элементов, а также пересчета расположения элементов после применения фильтра. Фильтрация позволяет отображать только те элементы, которые соответствуют определённым критериям, не перезагружая страницу.

Элементы для Masonry обычно имеют общий контейнер, например:

<div class="grid">
  <div class="grid-item category-a">Элемент 1</div>
  <div class="grid-item category-b">Элемент 2</div>
  <div class="grid-item category-a">Элемент 3</div>
  <div class="grid-item category-c">Элемент 4</div>
</div>

Инициализация Masonry:

const grid = document.querySelector('.grid');
const msnry = new Masonry(grid, {
  itemSelector: '.grid-item',
  columnWidth: '.grid-item',
  percentPosition: true
});

Добавление фильтров

Фильтры можно реализовать через атрибуты или классы элементов. Основная идея — при выборе фильтра скрывать элементы, которые не соответствуют критерию, и пересчитывать макет.

function filterItems(category) {
  const items = grid.querySelectorAll('.grid-item');
  items.forEach(item => {
    if (category === 'all' || item.classList.contains(category)) {
      item.style.display = '';
    } else {
      item.style.display = 'none';
    }
  });
  msnry.layout();
}

В этом примере:

  • category === 'all' — показывает все элементы.
  • item.style.display = '' — возвращает элемент в поток документа.
  • msnry.layout() — пересчитывает расположение видимых элементов после изменения отображения.

Управление состоянием фильтров

Для поддержания состояния фильтров используется отдельная переменная:

let activeFilter = 'all';

const filterButtons = document.querySelectorAll('.filter-button');
filterButtons.forEach(button => {
  button.addEventListener('click', () => {
    activeFilter = button.dataset.filter;
    filterItems(activeFilter);
    updateActiveClass(button);
  });
});

function updateActiveClass(button) {
  filterButtons.forEach(btn => btn.classList.remove('active'));
  button.classList.add('active');
}

Здесь:

  • activeFilter хранит текущий выбранный фильтр.
  • data-filter на кнопках соответствует классам элементов.
  • Функция updateActiveClass обновляет визуальное состояние кнопок.

Анимация при фильтрации

Для плавного исчезновения и появления элементов используется CSS-переход:

.grid-item {
  transition: transform 0.3s, opacity 0.3s;
}

.grid-item.hidden {
  opacity: 0;
  transform: scale(0.8);
  pointer-events: none;
}

Функция фильтрации с анимацией:

function filterItemsAnimated(category) {
  const items = grid.querySelectorAll('.grid-item');
  items.forEach(item => {
    if (category === 'all' || item.classList.contains(category)) {
      item.classList.remove('hidden');
    } else {
      item.classList.add('hidden');
    }
  });
  msnry.layout();
}

Преимущество такого подхода: элементы исчезают и появляются плавно, сохраняя общий визуальный ритм сетки.

Сложные фильтры

Можно комбинировать несколько фильтров, используя массивы и логические условия:

let activeFilters = [];

function toggleFilter(filter) {
  const index = activeFilters.indexOf(filter);
  if (index > -1) {
    activeFilters.splice(index, 1);
  } else {
    activeFilters.push(filter);
  }
  applyMultipleFilters();
}

function applyMultipleFilters() {
  const items = grid.querySelectorAll('.grid-item');
  items.forEach(item => {
    const matches = activeFilters.length === 0 || activeFilters.some(f => item.classList.contains(f));
    item.style.display = matches ? '' : 'none';
  });
  msnry.layout();
}
  • activeFilters — массив всех активных фильтров.
  • Метод some проверяет, соответствует ли элемент хотя бы одному фильтру.
  • Такой подход позволяет гибко комбинировать фильтры, включая возможность «показать все» при пустом массиве.

События и динамическое обновление

Masonry поддерживает события layoutComplete и removeComplete, которые помогают отслеживать изменения после фильтрации:

msnry.on('layoutComplete', function() {
  console.log('Макет обновлён после фильтрации');
});

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

Интеграция с библиотекой imagesLoaded

Для корректной работы с изображениями нужно учитывать, что Masonry рассчитывает макет после загрузки контента. Используется библиотека imagesLoaded:

imagesLoaded(grid, () => {
  msnry.layout();
});

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

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

  • Минимизировать количество операций layout: вызывать msnry.layout() только после того, как все элементы скрыты или показаны.
  • Использовать CSS-классы для анимации, вместо изменения inline-стилей, для более плавного отображения.
  • Кешировать элементы и фильтры, чтобы не выполнять повторные DOM-запросы при каждом клике.

Эти методы обеспечивают стабильную работу фильтров даже при больших сетках с сотнями элементов.