Custom Highlight API: стилизация выделения текста

Магия без лишних тегов: Укрощаем Custom Highlight API

Признайся, сколько раз тебе приходилось реализовывать поиск по странице или динамическую подсветку синтаксиса? Это всегда превращалось в сомнительное удовольствие. Ты берешь текст, режешь его на куски, оборачиваешь каждое совпадение в несчастный span с классом и молишься, чтобы верстка не поплыла. А если пользователь решит скопировать этот текст? А если там сложные вложенные теги? Добро пожаловать в мир боли.

Сегодня мы разберем Custom Highlight API — технологию, которая позволяет «красить» текст прямо поверх DOM-дерева, не внося в него никаких изменений. Это как накладывать фильтр в Instagram: картинка та же, но выглядит иначе.

Как мы страдали раньше

До появления этого API у нас был ровно один путь: мутация DOM. Нам приходилось программно находить текстовые узлы, разбивать их и вставлять новые элементы. Это порождало кучу проблем:

  • Производительность: На больших документах перерисовка тысяч мелких элементов вешала браузер намертво.
  • Слом структуры: Скрипты, полагающиеся на текстовое содержимое (например, скринридеры или старые добрые парсеры), сходили с ума от обилия лишних тегов.
  • Стилистический хаос: Управлять специфичностью вложенных спанов было тем еще квестом. Чтобы избежать конфликтов, мы пытались использовать псевдоклассы :is() и :where() для чистого кода, но корень проблемы оставался — лишняя разметка.

Короче, мы создавали монстров из тегов ради банальной смены цвета фона.

Как делать правильно в 2026 году

Custom Highlight API предлагает элегантный подход. Мы работаем с объектами Range (диапазонами), которые указывают «от этого символа до того», и регистрируем их в специальном реестре CSS.highlights. Браузер сам отрисовывает выделение на виртуальном слое, не трогая твои священные теги.

Это работает чертовски быстро и позволяет разделять логику (какие данные выделить) и визуализацию (как выделить). Чтобы твои стили выделения не перекрывались другими правилами, можно даже внедрить каскадные слои CSS (@layer) для управления приоритетами.

Алгоритм действий простой:

  • Создаем один или несколько объектов Range.
  • Группируем их в объект Highlight.
  • Регистрируем этот Highlight в системе под уникальным именем.
  • Стилизуем это имя в CSS через псевдоэлемент ::highlight().

Готовый сниппет кода

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


// JS часть: находим и регистрируем
const textNode = document.querySelector('p').firstChild;
const searchWord = "Highlight";
const ranges = [];

let pos = 0;
while ((pos = textNode.textContent.indexOf(searchWord, pos)) >= 0) {
    const range = new Range();
    range.setStart(textNode, pos);
    range.setEnd(textNode, pos + searchWord.length);
    ranges.push(range);
    pos += searchWord.length;
}

// Создаем объект подсветки и регистрируем его
const searchHighlight = new Highlight(...ranges);
CSS.highlights.set("my-custom-search", searchHighlight);

/* CSS часть: задаем стиль */
::highlight(my-custom-search) {
    background-color: #ffde03;
    color: #000;
    text-decoration: underline;
}

Частая ошибка новичков

Самый частый фейл — забывать о динамике. Custom Highlight API «живой», но Range привязан к конкретным узлам и смещениям. Если содержимое элемента изменится (например, через innerHTML), твои диапазоны могут «съехать» или стать невалидными.

Многие новички создают Highlight один раз при загрузке страницы и удивляются, почему после обновления данных подсветка ведет себя неадекватно. Запомни: если текст в DOM изменился, тебе нужно либо пересчитать индексы в существующих Range, либо очистить старый Highlight через CSS.highlights.clear() и создать новые диапазоны. Не заставляй браузер подсвечивать пустоту!

И еще: не пытайся стилизовать в ::highlight свойства, влияющие на геометрию (типа margin или width). Доступны только цвета, тени, украшения текста и фон. Это сделано ради той самой производительности, к которой мы так стремились.

🔥 Больше фишек, готовых сниппетов и передовых подходов к CSS мы публикуем в нашем Telegram-канале. Подписывайтесь, чтобы не пропустить!

🚀 Прокачай свой код

Готовые CSS-сниппеты, разбор продвинутых фишек и эксклюзивные материалы — в нашем Telegram-канале.

Подписаться
error: Content is protected !!
Прокрутить вверх