Комбинированная фильтрация

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


Основные принципы

Комбинированная фильтрация основывается на двух ключевых моментах:

  1. Независимые фильтры – каждый фильтр отвечает за определённую категорию, например, цвет, тип или цену.
  2. Объединение условий – элементы отображаются только если они соответствуют всем активным фильтрам одновременно.

Isotope использует метод filter, которому можно передавать функцию или CSS-селектор. Для комбинированной фильтрации чаще всего применяется функция, которая проверяет несколько условий.


Структура фильтров

Фильтры обычно группируются по категориям. Пример структуры HTML:

<div class="filters">
  <div class="filter-group" data-filter-group="color">
    <button data-filter="*">Все цвета</button>
    <button data-filter=".red">Красный</button>
    <button data-filter=".blue">Синий</button>
  </div>

  <div class="filter-group" data-filter-group="type">
    <button data-filter="*">Все типы</button>
    <button data-filter=".shirt">Футболки</button>
    <button data-filter=".pants">Штаны</button>
  </div>
</div>

<div class="grid">
  <div class="grid-item red shirt">Футболка красная</div>
  <div class="grid-item blue pants">Штаны синие</div>
  <div class="grid-item red pants">Штаны красные</div>
</div>

Каждая группа фильтров имеет атрибут data-filter-group, а каждая кнопка — свой data-filter. Такой подход позволяет хранить состояние фильтров и применять их комбинированно.


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

Для работы с фильтрацией создаётся экземпляр Isotope:

var iso = new Isotope('.grid', {
  itemSelector: '.grid-item',
  layoutMode: 'fitRows'
});

Здесь itemSelector указывает элементы, которые будут сортироваться и фильтроваться, а layoutMode определяет алгоритм расположения элементов (fitRows или masonry).


Механизм комбинированной фильтрации

Комбинированная фильтрация требует хранения активных фильтров для каждой группы и объединения их при применении:

var filters = {};

var filterGroups = document.querySelectorAll('.filter-group');
filterGroups.forEach(function(group) {
  group.addEventListener('click', function(event) {
    if (!event.target.matches('button')) return;

    var filterGroup = group.getAttribute('data-filter-group');
    filters[filterGroup] = event.target.getAttribute('data-filter');

    // Объединяем фильтры
    var filterValue = concatFilters(filters);
    iso.arrange({ filter: filterValue });
  });
});

function concatFilters(filters) {
  var allFilters = Object.values(filters);
  return allFilters.join('');
}

Функция concatFilters объединяет фильтры через конкатенацию CSS-классов. Если значение фильтра *, оно не влияет на результат.


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

Для более сложных условий можно использовать функцию:

iso.arrange({
  filter: function(itemElem) {
    var colorFilter = filters.color || '*';
    var typeFilter = filters.type || '*';

    var matchesColor = colorFilter === '*' || itemElem.classList.contains(colorFilter.substring(1));
    var matchesType = typeFilter === '*' || itemElem.classList.contains(typeFilter.substring(1));

    return matchesColor && matchesType;
  }
});

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


Работа с активными кнопками

Для улучшения UX полезно подсвечивать активные фильтры:

var buttons = document.querySelectorAll('.filter-group button');
buttons.forEach(function(btn) {
  btn.addEventListener('click', function() {
    var siblings = btn.parentNode.querySelectorAll('button');
    siblings.forEach(function(sib) { sib.classList.remove('is-checked'); });
    btn.classList.add('is-checked');
  });
});

Класс is-checked визуально отмечает выбранный фильтр, обеспечивая понятное взаимодействие с пользователем.


Пример комбинированной фильтрации с анимацией

Isotope поддерживает плавную анимацию при фильтрации и сортировке:

iso.arrange({
  filter: concatFilters(filters),
  transitionDuration: '0.6s'
});

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


Поддержка нескольких групп и исключений

Комбинированная фильтрация может включать несколько групп и исключения:

  • Несколько групп: объединение фильтров по CSS-селекторам или функциями.
  • Исключения: можно использовать :not() в селекторах, чтобы исключить элементы.

Пример исключения:

iso.arrange({ filter: '.shirt:not(.red)' });

Отобразятся все футболки, кроме красных.


Интеграция с динамическим контентом

При добавлении новых элементов через AJAX или динамически необходимо вызвать appended:

var newItems = document.querySelectorAll('.grid-item-new');
iso.appended(newItems);
iso.arrange({ filter: concatFilters(filters) });

Это гарантирует, что новые элементы будут учитывать текущие фильтры.


Рекомендации по оптимизации

  • Хранить состояние фильтров в объекте, чтобы избежать повторного перебора DOM.
  • Использовать функцию фильтрации для сложных условий.
  • Поддерживать * как универсальный фильтр, чтобы можно было сбрасывать группы.
  • При больших наборах данных применять requestAnimationFrame для плавности анимации.

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