Подсветка результатов в Slim Sel ect используется для визуального выделения совпадений между введённым поисковым запросом и текстом элементов списка. Такая функциональность особенно полезна при работе с длинными списками, динамическим поиском и множественным выбором, поскольку позволяет быстрее находить нужные варианты.
Во время ввода текста библиотека анализирует содержимое опций и
автоматически выделяет совпадающие фрагменты. Обычно для этого
применяется HTML-разметка с тегами <mark>,
<span> или специализированными CSS-классами.
Подсветка может работать:
Slim Select выполняет поиск по тексту опций через внутренний механизм фильтрации. После обнаружения совпадений библиотека может:
Пример:
<select id="countries">
<option>Germany</option>
<option>Georgia</option>
<option>Greece</option>
</select>
new SlimSelect({
select: '#countries'
})
При вводе:
geo
Выпадающий список может визуально преобразоваться в:
<div class="option">
<mark>Geo</mark>rgia
</div>
В базовой конфигурации Slim Select может использовать встроенную логику отображения результатов поиска. В зависимости от версии библиотеки совпадения либо подсвечиваются автоматически, либо требуется кастомная реализация через renderer.
Типичный механизм:
new SlimSelect({
select: '#countries',
searchHighlight: true
})
После активации:
Подсветка результатов почти всегда оформляется через CSS.
Пример:
.ss-highlight {
background-color: yellow;
color: black;
font-weight: bold;
}
Если Slim Select использует класс .ss-highlight, все
совпадения автоматически получат указанный стиль.
Другой пример:
mark {
background: #ffe08a;
padding: 2px 4px;
border-radius: 3px;
}
Наиболее гибкий способ настройки — переопределение рендеринга элементов.
Пример:
const slim = new SlimSelect({
select: '#countries',
events: {
search: (search, currentData) => {
return currentData
}
}
})
Далее создаётся собственная функция обработки текста.
Базовый алгоритм подсветки:
function highlight(text, query) {
if (!query) return text
const regex = new RegExp(`(${query})`, 'gi')
return text.replace(regex, '<mark>$1</mark>')
}
Принцип работы:
Пример интеграции:
const searchInput = 'geo'
const result = highlight('Georgia', searchInput)
console.log(result)
Результат:
<mark>Geo</mark>rgia
При использовании массива данных:
new SlimSelect({
select: '#users',
data: [
{ text: 'Alexander' },
{ text: 'Alexey' },
{ text: 'Alexa' }
]
})
Подсветка может формироваться во время генерации элементов:
function renderOption(option, search) {
return `
<div class="option">
${highlight(option.text, search)}
</div>
`
}
Функция может выделять все найденные участки строки.
Пример:
highlight('Banana', 'an')
Результат:
B<mark>an</mark><mark>an</mark>a
Благодаря глобальному флагу g регулярное выражение
находит все совпадения.
Флаг i позволяет выполнять поиск без учёта регистра.
Пример:
const regex = new RegExp(`(${query})`, 'gi')
Теперь:
Geo
geo
GEO
gEo
будут считаться одинаковыми запросами.
Пользователь может вводить символы регулярных выражений:
+
*
?
[
]
Без экранирования поиск вызовет ошибку.
Надёжная реализация:
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
Использование:
function highlight(text, query) {
const escaped = escapeRegExp(query)
const regex = new RegExp(`(${escaped})`, 'gi')
return text.replace(regex, '<mark>$1</mark>')
}
Иногда поиск должен учитывать несколько слов одновременно.
Пример запроса:
new york
Разделение запроса:
const parts = query.split(' ')
Формирование выражения:
const regex = new RegExp(`(${parts.join('|')})`, 'gi')
Теперь подсвечиваются оба слова:
<mark>New</mark> <mark>York</mark>
Иногда необходимо выделять только совпадения в начале текста.
Пример:
const regex = new RegExp(`^(${query})`, 'gi')
Теперь:
Germany
при поиске:
ger
подсветится, а:
Bulgaria
нет.
Slim Select поддерживает группы options.
Пример:
<select id="products">
<optgroup label="Phones">
<option>iPhone</option>
<option>Samsung</option>
</optgroup>
<optgroup label="Laptops">
<option>MacBook</option>
</optgroup>
</select>
Подсветка может применяться:
Пример:
highlight('Phones', 'pho')
Результат:
<mark>Pho</mark>nes
При загрузке данных с сервера подсветка обычно выполняется после получения результатов.
Пример:
fetch('/api/users?q=alex')
.then(response => response.json())
.then(data => {
const prepared = data.map(item => ({
text: highlight(item.name, 'alex'),
value: item.id
}))
})
Нельзя напрямую вставлять пользовательский ввод в HTML.
Опасный пример:
return text.replace(regex, `<mark>${query}</mark>`)
Если пользователь введёт HTML:
<script>alert(1)</script>
возможна XSS-атака.
Безопаснее использовать экранирование HTML.
Пример функции:
function escapeHtml(text) {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}
Безопасная реализация:
function highlight(text, query) {
const escapedText = escapeHtml(text)
const escapedQuery = escapeRegExp(query)
const regex = new RegExp(`(${escapedQuery})`, 'gi')
return escapedText.replace(regex, '<mark>$1</mark>')
}
Подсветка может существенно нагружать интерфейс при работе с тысячами элементов.
Основные причины:
Плохой вариант:
items.forEach(item => {
const regex = new RegExp(query, 'gi')
})
Хороший вариант:
const regex = new RegExp(query, 'gi')
items.forEach(item => {
})
Пример:
const filtered = data.slice(0, 50)
Подсветка применяется только к первым найденным элементам.
Пример:
function debounce(callback, delay) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
callback(...args)
}, delay)
}
}
Некоторые интерфейсы отображают подсветку не только в списке, но и внутри уже выбранных значений.
Пример:
<div class="selected">
<mark>Alex</mark>ander
</div>
Такой подход полезен:
Тег <mark> подходит не всегда.
Альтернативный вариант:
return text.replace(
regex,
'<span class="highlight">$1</span>'
)
CSS:
.highlight {
background: #ffd54f;
font-weight: 600;
}
Можно добавить плавное появление совпадений.
Пример:
.highlight {
background: #ffeb3b;
transition: background 0.3s ease;
}
Или:
.highlight {
animation: flash 0.4s ease;
}
@keyframes flash {
fr om {
background: orange;
}
to {
background: yellow;
}
}
При работе с кириллицей, японскими символами и другими языками важно использовать Unicode-совместимые выражения.
Пример:
const regex = new RegExp(`(${escapedQuery})`, 'giu')
Флаг u обеспечивает корректную работу Unicode.
Иногда поиск должен находить:
moskva
для:
Москва
В таком случае используется дополнительный слой преобразования текста.
Пример:
function transliterate(text) {
return text
.replace(/ш/g, 'sh')
.replace(/ч/g, 'ch')
}
После преобразования поиск выполняется уже по транслитерированной строке.
Для fuzzy search можно использовать библиотеки:
Пример:
const fuse = new Fuse(data, {
keys: ['text']
})
Результаты поиска:
const results = fuse.search('alx')
Затем совпадения подсвечиваются вручную.
Пример:
const fuse = new Fuse(users, {
keys: ['text'],
includeMatches: true
})
Поиск:
const results = fuse.search('alex')
Получение совпадений:
results.forEach(result => {
console.log(result.matches)
})
Массив matches содержит индексы совпавших символов,
которые можно использовать для точной подсветки.
Пример:
function highlightByIndexes(text, indexes) {
let result = ''
for (let i = 0; i < text.length; i++) {
const matched = indexes.some(([start, end]) =>
i >= start && i <= end
)
if (matched) {
result += `<mark>${text[i]}</mark>`
} else {
result += text[i]
}
}
return result
}
Если option уже содержит HTML:
{
text: '<b>Admin</b>'
}
необходимо аккуратно обрабатывать разметку, иначе подсветка может сломать DOM.
Лучший подход:
Ошибка:
<mark><mark>Alex</mark></mark>
Причина — повторный запуск highlight по уже обработанному HTML.
При полной перерисовке списка могут исчезать события:
element.innerHTML = html
Решение:
Запрос:
new york
может не находить:
New York
Решение:
text.replace(/\s+/g, ' ')
Крупные приложения часто выносят подсветку в отдельный модуль.
Пример структуры:
search/
├── highlight.js
├── escape.js
├── tokenizer.js
├── renderer.js
└── styles.css
Преимущества:
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
function escapeHtml(text) {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}
function highlight(text, query) {
if (!query) {
return escapeHtml(text)
}
const escapedText = escapeHtml(text)
const escapedQuery = escapeRegExp(query)
const regex = new RegExp(`(${escapedQuery})`, 'giu')
return escapedText.replace(
regex,
'<span class="highlight">$1</span>'
)
}
CSS:
.highlight {
background: #fff176;
color: #000;
border-radius: 2px;
padding: 0 2px;
}
Использование:
const result = highlight(
'Alexander Pushkin',
'alex'
)
console.log(result)
Результат:
<span class="highlight">Alex</span>ander Pushkin