Фильтрация элементов в Choices.js основана на двух уровнях: внутренний поисковый механизм и внешний контроль над набором данных. Внутри библиотеки используется fuzzy-поиск через движок, построенный на базе Fuse.js, что обеспечивает ранжирование результатов по степени совпадения, а не строгому равенству строк.
При вводе текста в поле поиска формируется запрос, который сравнивается с набором доступных вариантов. Каждый элемент оценивается по релевантности, после чего формируется отсортированный список результатов.
Ключевой особенностью является то, что логика фильтрации не фиксирована и может быть изменена через конфигурацию или полностью заменена внешней реализацией.
Поведение стандартного фильтра регулируется набором опций инициализации экземпляра Choices.
Определяет наличие поисковой строки внутри выпадающего списка.
true — поиск активенfalse — поиск отключён, отображается полный списокnew Choices(element, {
searchEnabled: true
});
Минимальное количество символов для запуска фильтрации.
new Choices(element, {
searchFloor: 2
});
При значении 2 фильтрация не выполняется до ввода двух
символов, что снижает нагрузку и количество промежуточных
вычислений.
Ограничивает количество отображаемых результатов.
new Choices(element, {
searchResultLimit: 10
});
Ограничение применяется после ранжирования результатов, что позволяет контролировать размер DOM и ускорять рендеринг.
Определяет сортировку результатов поиска.
new Choices(element, {
shouldSort: false
});
При отключённой сортировке порядок элементов сохраняется исходным, даже при наличии релевантного поиска.
Основная гибкость фильтрации достигается через передачу параметров в Fuse.js. Это позволяет изменять поведение fuzzy-поиска: чувствительность, веса полей, порог совпадения.
new Choices(element, {
fuseOptions: {
threshold: 0.2,
distance: 100,
keys: ['label', 'value']
}
});
Определяет степень «строгости» совпадения.
0.0 — только точные совпадения1.0 — максимально свободный fuzzy-поискНизкие значения увеличивают точность, высокие — расширяют результаты.
Позволяет выполнять поиск по нескольким полям объекта.
{
value: 'id',
label: 'name',
description: 'text'
}
new Choices(element, {
fuseOptions: {
keys: ['label', 'description']
}
});
Фильтрация становится многокритериальной, что особенно полезно при сложных структурах данных.
Контролирует максимальную дистанцию совпадений внутри строки. При увеличении значения допускаются более «разнесённые» совпадения символов.
Одним из распространённых подходов является нормализация данных до передачи в 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());
Для расширенной обработки применяются:
При сложных данных элемент часто содержит несколько значимых полей. Стандартный 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))
);
}
При увеличении объёма данных стандартная фильтрация может становиться узким местом. Основные оптимизационные стратегии:
searchResultLimitfunction 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
});
В этом случае результат зависит исключительно от порядка, заданного внешней фильтрацией или сервером.
На практике часто используется гибридный подход:
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 формируется слоями:
Такое разделение позволяет строить как простые выпадающие списки, так и сложные поисковые интерфейсы с полной кастомизацией правил отбора данных.