Кастомная логика фильтрации

Фильтрация элементов в Choices.js основана на двух уровнях: внутренний поисковый механизм и внешний контроль над набором данных. Внутри библиотеки используется fuzzy-поиск через движок, построенный на базе Fuse.js, что обеспечивает ранжирование результатов по степени совпадения, а не строгому равенству строк.

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

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


Базовые параметры управления поиском

Поведение стандартного фильтра регулируется набором опций инициализации экземпляра Choices.

searchEnabled

Определяет наличие поисковой строки внутри выпадающего списка.

  • true — поиск активен
  • false — поиск отключён, отображается полный список
new Choices(element, {
  searchEnabled: true
});

searchFloor

Минимальное количество символов для запуска фильтрации.

new Choices(element, {
  searchFloor: 2
});

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


searchResultLimit

Ограничивает количество отображаемых результатов.

new Choices(element, {
  searchResultLimit: 10
});

Ограничение применяется после ранжирования результатов, что позволяет контролировать размер DOM и ускорять рендеринг.


shouldSort

Определяет сортировку результатов поиска.

new Choices(element, {
  shouldSort: false
});

При отключённой сортировке порядок элементов сохраняется исходным, даже при наличии релевантного поиска.


Настройка Fuse.js через fuseOptions

Основная гибкость фильтрации достигается через передачу параметров в Fuse.js. Это позволяет изменять поведение fuzzy-поиска: чувствительность, веса полей, порог совпадения.

new Choices(element, {
  fuseOptions: {
    threshold: 0.2,
    distance: 100,
    keys: ['label', 'value']
  }
});

threshold

Определяет степень «строгости» совпадения.

  • 0.0 — только точные совпадения
  • 1.0 — максимально свободный fuzzy-поиск

Низкие значения увеличивают точность, высокие — расширяют результаты.


keys

Позволяет выполнять поиск по нескольким полям объекта.

{
  value: 'id',
  label: 'name',
  description: 'text'
}
new Choices(element, {
  fuseOptions: {
    keys: ['label', 'description']
  }
});

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


distance

Контролирует максимальную дистанцию совпадений внутри строки. При увеличении значения допускаются более «разнесённые» совпадения символов.


Расширение фильтрации через предобработку данных

Одним из распространённых подходов является нормализация данных до передачи в Choices.js. Это позволяет унифицировать поведение поиска без изменения внутренних механизмов.

Типичные преобразования:

  • приведение к нижнему регистру
  • удаление диакритики
  • транслитерация
  • очистка спецсимволов
function normalize(str) {
  return str
    .toLowerCase()
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '');
}

const items = rawItems.map(item => ({
  value: item.id,
  label: normalize(item.title)
}));

Такой подход снижает зависимость от настроек fuzzy-алгоритма и делает поведение поиска предсказуемым.


Полная замена фильтрации через внешний поиск

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

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

const instance = new Choices(element, {
  searchEnabled: false
});

Далее список формируется вручную:

function externalSearch(query) {
  const filtered = database.filter(item =>
    item.name.includes(query)
  );

  instance.setChoices(filtered, 'id', 'name', true);
}

Этот подход позволяет:

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

Кастомная логика через обработку события поиска

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

element.addEventListener('search', function(event) {
  const query = event.detail.value;

  const results = customFilter(query);

  instance.setChoices(results, 'id', 'name', true);
});

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


Условная фильтрация и бизнес-логика

Фильтрация часто требует учёта дополнительных условий:

  • права доступа
  • категории
  • региональные ограничения
  • состояние данных
function filterItems(query, user) {
  return items
    .filter(item => item.active)
    .filter(item => item.roles.includes(user.role))
    .filter(item =>
      item.name.toLowerCase().includes(query.toLowerCase())
    );
}

Choices.js в этом случае выступает как UI-обёртка, не участвующая в принятии решений о релевантности.


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

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

Для унификации используется нормализация:

const match = (a, b) =>
  a.toLowerCase().includes(b.toLowerCase());

Для расширенной обработки применяются:

  • Unicode normalization
  • удаление пробелов
  • замена символов

Поиск по нескольким полям и составным структурам

При сложных данных элемент часто содержит несколько значимых полей. Стандартный Fuse.js-подход позволяет учитывать их одновременно.

new Choices(element, {
  fuseOptions: {
    keys: [
      'label',
      'category',
      'tags'
    ]
  }
});

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

function multiFieldFilter(query, item) {
  const q = query.toLowerCase();

  return (
    item.label.toLowerCase().includes(q) ||
    item.category.toLowerCase().includes(q) ||
    item.tags.some(t => t.includes(q))
  );
}

Производительность при сложной фильтрации

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

  • ограничение searchResultLimit
  • дебаунс ввода
  • предварительная индексация данных
  • использование серверного поиска
function debounce(fn, delay) {
  let t;
  return function (...args) {
    clearTimeout(t);
    t = setTimeout(() => fn.apply(this, args), delay);
  };
}
input.addEventListener(
  'input',
  debounce(e => externalSearch(e.target.value), 300)
);

Отключение сортировки и контроль порядка результатов

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

new Choices(element, {
  shouldSort: false
});

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


Комбинированные стратегии фильтрации

На практике часто используется гибридный подход:

  • Fuse.js для базового fuzzy-поиска
  • предфильтрация по бизнес-правилам
  • постобработка результатов
  • ограничение выдачи
const fuse = new Fuse(items, {
  keys: ['label'],
  threshold: 0.3
});

function search(query) {
  return fuse.search(query)
    .map(r => r.item)
    .filter(item => item.active)
    .slice(0, 20);
}

Итоговая структура управления фильтрацией

Логика фильтрации в Choices.js формируется слоями:

  1. UI-уровень — ввод строки поиска
  2. Встроенный fuzzy-алгоритм — Fuse.js
  3. Конфигурация поведения — параметры поиска
  4. Внешняя логика — setChoices или серверный поиск

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