В основе выделения совпадений в Tom Select лежит механизм кастомного
рендеринга опций. Библиотека не ограничивается простым отображением
текста: каждая опция проходит через функцию render.option,
а выбранные элементы — через render.item. Именно на этом
уровне формируется визуальное представление совпавших фрагментов
поискового запроса.
Стандартное поведение предполагает, что текст опции сравнивается с
введённым запросом, после чего совпавшие части оборачиваются в
HTML-разметку. Обычно используется <span> или
<mark>, что позволяет визуально отделить релевантные
фрагменты от остального текста.
Поиск в Tom Select строится вокруг функции ранжирования
(score) и нормализации строк. На этапе ввода запроса
выполняются следующие шаги:
После этого каждая опция получает оценку соответствия. Совпадения не просто фиксируются по наличию подстроки — учитывается позиция, полнота совпадения и порядок символов.
Подсветка строится на основе тех же вычислений: библиотека определяет границы совпавших сегментов и передаёт их в рендер-функции.
В стандартной конфигурации Tom Select подсветка активируется автоматически. Внутренний алгоритм формирует HTML-фрагменты с выделением совпадающих частей запроса.
Пример поведения:
carCaribbean Carnival<span class="highlight">Car</span>ibbean <span class="highlight">Car</span>nivalПодсветка может отличаться в зависимости от настроек нормализации и используемого режима поиска.
Ключевая особенность заключается в том, что библиотека не просто заменяет подстроки, а использует результат token-based поиска, что делает подсветку более точной при сложных запросах.
Полный контроль над выделением совпадений достигается переопределением рендеринга.
new TomSelect("#select", {
render: {
option: function(data, escape) {
return `
<div>
<span class="title">${escape(data.text)}</span>
</div>
`;
},
item: function(data, escape) {
return `<div>${escape(data.text)}</div>`;
}
}
});
В таком виде подсветка отключается, поскольку не используется механизм разметки совпадений. Для восстановления выделения необходимо самостоятельно внедрить логику подсветки.
При кастомных шаблонах подсветка реализуется через обработку строки поиска. Основной подход — использование регулярных выражений.
function highlight(text, query) {
if (!query) return text;
const pattern = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'i');
return text.replace(pattern, '<span class="highlight">$1</span>');
}
Использование в render.option:
new TomSelect("#select", {
render: {
option: function(data, escape) {
return `
<div>
${highlight(escape(data.text), this.lastQuery)}
</div>
`;
}
}
});
Здесь важно учитывать контекст lastQuery, который
содержит текущее значение поиска.
При вводе составных запросов необходимо учитывать множественные совпадения. В таких случаях используется разбиение строки на слова:
function highlightMultiple(text, query) {
if (!query) return text;
const words = query
.trim()
.split(/\s+/)
.map(w => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
const pattern = new RegExp(`(${words.join('|')})`, 'gi');
return text.replace(pattern, '<mark>$1</mark>');
}
Такой подход позволяет выделять каждое совпадение независимо от порядка слов.
В языках с диакритическими символами стандартное сравнение строк становится недостаточным. Tom Select использует нормализацию Unicode, но при кастомной реализации подсветки требуется учитывать этот аспект вручную.
function normalize(str) {
return str
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
После нормализации поиск совпадений становится устойчивым к различным вариантам ввода, например:
cafecaféобрабатываются одинаково, но для корректной подсветки необходимо сохранять оригинальную строку и вычислять индексы совпадений на нормализованной версии.
Любая подсветка совпадений связана с генерацией HTML. Это требует обязательного экранирования входных данных.
function escapeHtml(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
Использование:
return highlight(escapeHtml(data.text), escapeHtml(query));
Без экранирования возможно внедрение нежелательного HTML-кода через пользовательский ввод или данные источника.
Визуальное оформление выделения обычно задаётся через CSS:
.highlight {
background: rgba(255, 230, 0, 0.4);
font-weight: 600;
border-radius: 2px;
padding: 0 2px;
}
При использовании <mark> можно полагаться на
встроенные стили браузера, однако кастомный класс даёт полный контроль
над внешним видом.
При загрузке данных через load подсветка должна
учитывать задержки и обновление состояния:
new TomSelect("#select", {
load: function(query, callback) {
fetch(`/api?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => callback(data));
},
render: {
option: function(data, escape) {
return `<div>${escape(data.name)}</div>`;
}
}
});
В таких сценариях важно, чтобы функция подсветки использовала актуальный запрос, иначе возможна рассинхронизация отображения и введённого текста.
При большом количестве опций и частых вводах подсветка становится критическим участком производительности. Основные узкие места:
input.Оптимизация достигается через:
render.Эффективная реализация подсветки всегда балансирует между точностью совпадений и стоимостью вычислений в момент ввода текста.