Сортировка и фильтрация одновременно

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


Подготовка элементов для сортировки и фильтрации

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

<div class="grid">
  <div class="grid-item" data-category="nature" data-price="20">Элемент 1</div>
  <div class="grid-item" data-category="architecture" data-price="50">Элемент 2</div>
  <div class="grid-item" data-category="nature" data-price="30">Элемент 3</div>
</div>
  • data-category — используется для фильтрации по категориям.
  • data-price — пример атрибута для сортировки по числовому значению.

Важно, чтобы все элементы имели однородную структуру атрибутов, иначе сортировка и фильтрация могут работать некорректно.


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

Для начала необходимо создать экземпляр Masonry и подключить элементы сетки:

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

const msnry = new Masonry(grid, {
  itemSelector: '.grid-item',
  columnWidth: '.grid-item',
  percentPosition: true,
  gutter: 10
});
  • itemSelector определяет, какие элементы будут участвовать в сетке.
  • columnWidth может быть фиксированным или привязанным к ширине элемента.
  • percentPosition: true позволяет сетке быть отзывчивой.

Фильтрация элементов

Фильтрация подразумевает отображение только тех элементов, которые соответствуют критериям. Простейший способ — управление видимостью через CSS:

function filterItems(category) {
  const items = grid.querySelectorAll('.grid-item');
  items.forEach(item => {
    if (category === 'all' || item.dataset.category === category) {
      item.style.display = '';
    } else {
      item.style.display = 'none';
    }
  });
  msnry.layout();
}
  • display: none исключает элемент из сетки, но Masonry после вызова layout() корректно перераспределяет оставшиеся элементы.
  • Параметр 'all' позволяет отображать все элементы.

Сортировка элементов

Для сортировки удобно использовать массив элементов и функцию sort():

function sortItems(attribute, ascending = true) {
  const items = Array.from(grid.querySelectorAll('.grid-item'));
  items.sort((a, b) => {
    const aVal = parseFloat(a.dataset[attribute]);
    const bVal = parseFloat(b.dataset[attribute]);
    return ascending ? aVal - bVal : bVal - aVal;
  });
  items.forEach(item => grid.appendChild(item));
  msnry.reloadItems();
  msnry.layout();
}
  • grid.appendChild(item) перемещает элемент в DOM, Masonry обновляет позиции.
  • reloadItems() сообщает Masonry, что порядок элементов изменился.
  • layout() перераспределяет элементы в новой последовательности.

Совмещение фильтрации и сортировки

При одновременном использовании фильтров и сортировки важно сохранять текущее состояние фильтра при изменении порядка элементов:

let currentFilter = 'all';
let currentSort = { attribute: 'price', ascending: true };

function updateGrid() {
  const items = Array.from(grid.querySelectorAll('.grid-item'));

  // Применяем фильтр
  const filteredItems = items.filter(item => 
    currentFilter === 'all' || item.dataset.category === currentFilter
  );

  // Применяем сортировку к фильтрованным элементам
  filteredItems.sort((a, b) => {
    const aVal = parseFloat(a.dataset[currentSort.attribute]);
    const bVal = parseFloat(b.dataset[currentSort.attribute]);
    return currentSort.ascending ? aVal - bVal : bVal - aVal;
  });

  // Скрываем все элементы, затем отображаем отфильтрованные
  items.forEach(item => item.style.display = 'none');
  filteredItems.forEach(item => {
    item.style.display = '';
    grid.appendChild(item);
  });

  msnry.reloadItems();
  msnry.layout();
}
  • Функция updateGrid() объединяет оба действия в один поток, исключая рассинхронизацию.
  • currentFilter и currentSort позволяют динамически изменять критерии без пересоздания сетки.

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

При больших сетках рекомендуется:

  1. Использовать display: none вместо удаления элементов из DOM — это быстрее.
  2. Минимизировать вызовы layout() — лучше вызвать один раз после фильтрации и сортировки.
  3. Хранить ссылки на элементы в массиве, чтобы не выполнять многократные DOM-запросы.
  4. Для сложной фильтрации можно применять классы вместо стилей, комбинируя с CSS transitions для плавной анимации.

Анимация переходов

Masonry хорошо работает с анимациями:

.grid-item {
  transition: transform 0.4s, opacity 0.4s;
}
  • Изменение позиции элементов сопровождается плавным перемещением.
  • opacity можно использовать при скрытии/отображении элементов для более мягкой фильтрации.

Поддержка динамического добавления элементов

Если новые элементы добавляются после инициализации:

function addNewItem(element) {
  grid.appendChild(element);
  msnry.appended(element);
  updateGrid(); // применяем текущие фильтр и сортировку
}
  • Метод appended() сообщает Masonry о новых элементах, чтобы корректно рассчитать позиции.
  • Последующий вызов updateGrid() гарантирует, что новые элементы соответствуют текущим фильтрам и сортировке.

Итоговая структура взаимодействия

  1. Сбор элементов и определение атрибутов для фильтрации и сортировки.
  2. Инициализация Masonry с базовыми параметрами.
  3. Создание функций фильтрации и сортировки с управлением отображением и DOM.
  4. Объединение в одну функцию updateGrid(), которая учитывает оба состояния.
  5. Оптимизация производительности и анимации для больших сеток.
  6. Обработка динамического добавления элементов через appended() и повторное применение текущих правил.

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