Написать пост

Стилизация участков текста с помощью CSS Custom Highlight API

В этой статье я расскажу, как с помощью CSS Custom Highlight API можно стилизовать выделенные диапазоны текста, а также разберу теорию на практическом примере.

Обложка поста Стилизация участков текста с помощью CSS Custom Highlight API

Задача по настройке внешнего вида диапазонов текста часто встречается в работе Frontend-разработчика.

В этой статье мы рассмотрим стилизацию участков текста с помощью CSS Custom Highlight API, а также изучим практическую сторону создания диапазонов выделения.

Обозначение орфографических и грамматических ошибок красивой волнистой линией в текстовых редакторах, таких как Google Docs, Word или Dropbox Paper — яркий пример стилизации отдельно взятых частей текста. Однако стилизовать отдельные участки нужно не только для того, чтобы указать пользователю на ошибки в текстах. Это полезно и во многих других случаях: например, в визуализации специфических данных в отчётах, обозначении частей кода и даже в образовательных целях, когда нужно выделить информацию на странице.

Еще одна распространенная задача — создание функции поиска и выделения текста, когда пользователь ввёл текст в поле поиска. Нажмите Ctrl/⌘+ F в веб-браузере прямо сейчас и введите что-нибудь и вы увидите, как текст из этой статьи будет подсвечиваться.

Недавно я наткнулся на статьи, в которых рассказывалось, как решить эту задачу. Я захотел разобраться в этой теме и адаптировал статьи на русский язык. Подробно я остановился на вещах, которые мне показались наиболее сложными и важными. 

CSS Custom Highlight API расширяет концепцию других псевдо-элементов выделения, таких как ::selection, ::spelling-error, ::grammar-error, и ::target-text. Он предоставляет способ создания и стилизации произвольных Range-объектов, не ограничиваясь диапазонами, определяемыми браузером.

Используя CSS Custom Highlight API, вы можете создавать текстовые диапазоны и выделять их, не влияя на структуру DOM на странице. Я считаю, что в этом главный плюс этого инструмента.

Оформление

Чтобы оформить диапазоны текста на веб-странице с помощью CSS Custom Highlight API, нужно выполнить четыре шага:

1. Создать Range-объекты.

2. Создать Highlight-объекты для этих диапазонов.

3. Зарегистрировать выделения с помощью HighlightRegistry.

4. Стилизовать текст с помощью псевдоэлемента ::highlight().

Дальше я подробно остановлюсь на каждом из этих процессов.

Создание Range диапазонов

Первый шаг — указание текстовых диапазонов, которые нужно стилизовать. Для этого мы создаём объекты Range в JavaScript. Взгляните на пример ниже:

			const parentElement = document.getElementById("container");
const parent = parentElement.childNodes[0];
/* Нужно обязательно указывать .childNodes[0], чтобы работать именно с текстовым узлом, а не узлом-элементом */

const rangeOne = new Range();
/* Cоздание нового Range-объекта обязательно требует оператор new */
rangeOne.setStart(parent, 20);
/* Начало первого диапазона */
rangeOne.setEnd(parent, 30);
/* Конец первого диапазона */

const rangeTwo = new Range();
/* Создание второго диапазона */
rangeTwo.setStart(parent, 45);
/* Начало второго диапазона */
rangeTwo.setEnd(parent, 70);
/* Конец второго диапазона */

		

Создание Highlight-объектов для диапазонов

Следующий этап — организация инстанций Highlight-объектов для ваших текстовых диапазонов. Highlight — это программа, применяемая для создания набора диапазонов, которые будут стилизованы на веб-странице.

			const highlightFirst = new Highlight(rangeOne);
const highlightSecond = new Highlight(rangeTwo);
		

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

			const highlight = new Highlight(rangeOne, rangeTwo);
		

Регистрация выделений с помощью HighlightRegistry

После создания выделений, их надо зарегистрировать с помощью HighlightRegistry, которая доступна как CSS.highlights. CSS.highlights — это объект, напоминающий структуру класса Map, он позволяет регистрировать выделения с уникальными идентификаторами.

			CSS.highlights.set("user-1-highlight", highlightFirst );
CSS.highlights.set("user-2-highlight", highlightSecond );
		

В приведённом выше фрагменте кода строки "user-1-highlight" и "user-2-highlight" — это пользовательские идентификаторы, которые можно использовать в CSS для применения стилей к зарегистрированным выделениям.

Вы можете зарегистрировать в реестре столько выделений, сколько вам нужно, а также удалить выделенные элементы и очистить весь реестр.

			// Удаление выделения
CSS.highlights.delete("user-1-highlight");

// Очищение выделений
CSS.highlights.clear();
		

Стилизация с помощью псевдоэлемента ::highlight()

Последний шаг — стилизация зарегистрированных выделений. Это делается с помощью ::highlight() псевдоэлемента.

Например, для стилизации user-1-highlight, зарегистрированного на предыдущем шаге, код будет таким:

			::highlight(user-1-highlight) {
  background-color: #f06;
  color: white;
}


::highlight(user-2-highlight) {
  background-color: rgb(255, 255, 255);
  color: #f06;
}
		

Результат

Стилизация участков текста с помощью CSS Custom Highlight API 1
Результат выделения текста с помощью ::highlight() 

Как и ::selection, подмножество свойств CSS может использоваться только с ::highlight() псевдоэлементом:

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

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

В этом примере показано, как использовать CSS Custom Highlight API для выделения результатов поиска.

Давайте рассмотрим, как этот механизм может использоваться на практике.

Сделаем текст с выделением результатов поиска.

Приведённый ниже фрагмент HTML-кода определяет поле поиска и статью с несколькими абзацами текста:

			  <label>Search within text <input id="search" type="text" /></label>
  <article>
    <p>
      One sector that has felt this tidal wave of digital transformation particularly acutely is the retail industry. The
      emergence of e-commerce platforms and the revolution that is online shopping have caused tumultuous ripple effects on
      traditional brick-and-mortar stores. These traditional retailers have seen the race to online marketplaces as a survival
      requirement in today's digital economy. The immediacy of this requirement for change has propelled brick-and-mortar
      shops into developing robust web platforms and mobile applications to maintain their relevance in the industry.
      Moreover, to truly achieve a competitive edge, these businesses have begun to explore, understand, and utilize the
      environs of digital marketing and advertising. As they closely monitor consumer preferences and their growing demand for
      convenience and instant gratification, these retailers are pushed to adapt their delivery systems by integrating digital
      automation and strategic scaling plans.
      One sector that has felt this tidal wave of digital transformation particularly acutely is the retail industry. The
      emergence of e-commerce platforms and the revolution that is online shopping have caused tumultuous ripple effects on
      traditional brick-and-mortar stores. These traditional retailers have seen the race to online marketplaces as a survival
      requirement in today's digital economy. The immediacy of this requirement for change has propelled brick-and-mortar
      shops into developing robust web platforms and mobile applications to maintain their relevance in the industry.
      Moreover, to truly achieve a competitive edge, these businesses have begun to explore, understand, and utilize the
      environs of digital marketing and advertising. As they closely monitor consumer preferences and their growing demand for
      convenience and instant gratification, these retailers are pushed to adapt their delivery systems by integrating digital
      automation and strategic scaling plans.
    </p>
    <p>
      The concept of digital transformation represents a phenomenon that now pervades various industry sectors worldwide. It
      has become more of an imperative requirement than a flexible preference. This transformation process has been triggered
      by an accelerated influx of innovative technologies and data-driven strategies, thrusting businesses into a new era of
      operational and strategic methodology. In the face of this technological disruption, businesses across the globe are
      confronted with the ongoing challenge of reimagining their traditionally established practices and identifying ways to
      integrally embed digital methods into their core operations. Both the introduction and reach of novel digital tools have
      imprinted a necessity of change onto the very fabric of global industries, thereby instigating a serious reevaluation of
      previous business models and strategies.
      The concept of digital transformation represents a phenomenon that now pervades various industry sectors worldwide. It
      has become more of an imperative requirement than a flexible preference. This transformation process has been triggered
      by an accelerated influx of innovative technologies and data-driven strategies, thrusting businesses into a new era of
      operational and strategic methodology. In the face of this technological disruption, businesses across the globe are
      confronted with the ongoing challenge of reimagining their traditionally established practices and identifying ways to
      integrally embed digital methods into their core operations. Both the introduction and reach of novel digital tools have
      imprinted a necessity of change onto the very fabric of global industries, thereby instigating a serious reevaluation of
      previous business models and strategies.
    </p>
    <p>
      However, digital transformation is not a process that simply involves the superficial overlay of new technologies onto
      existing systems. It represents a more profound shift that must originate from within the organization itself - a
      cultural metamorphosis that encourages change, curiosity, and constant challenges to the status quo. The process
      requires businesses to create an environment of continuous learning and innovation, where employees feel comfortable
      adapting to and growing alongside technological advancements. This ongoing, employee-focused digitization effort is a
      critical component of the broader transformation agenda. This internal digital transformation must harmoniously coexist
      with the external technological advancements to truly harness the potential of digital technology and data-driven
      decision making. Ultimately, for a successful digital transformation journey, it is vital to hold not just the
      appropriate tech tools, but also to nourish the right organizational mindset.
      However, digital transformation is not a process that simply involves the superficial overlay of new technologies onto
      existing systems. It represents a more profound shift that must originate from within the organization itself - a
      cultural metamorphosis that encourages change, curiosity, and constant challenges to the status quo. The process
      requires businesses to create an environment of continuous learning and innovation, where employees feel comfortable
      adapting to and growing alongside technological advancements. This ongoing, employee-focused digitization effort is a
      critical component of the broader transformation agenda. This internal digital transformation must harmoniously coexist
      with the external technological advancements to truly harness the potential of digital technology and data-driven
      decision making. Ultimately, for a successful digital transformation journey, it is vital to hold not just the
      appropriate tech tools, but also to nourish the right organizational mindset.
    </p>
  </article>
  

		

Для прослушивания input-события в поле поиска используется JavaScript. При запуске события код находит совпадения для введённого текста. Затем он создает диапазоны для совпадений и использует CSS Custom Highlight API для создания и регистрации search-results объекта highlight:

			// Получаем ссылку на элемент с идентификатором "search"
const search = document.getElementById("search");
// Получаем ссылку на первый элемент  на странице
const article = document.querySelector("article");


// Находим все текстовые узлы внутри элемента, с которыми будем работать
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) {
  allTextNodes.push(currentNode);
  currentNode = treeWalker.nextNode();
}


// Слушаем событие ввода текста в поле поиска
search.addEventListener("input", () => {
  // Если CSS Custom Highlight API не поддерживается в браузере,
  // выводим сообщение и выходим из обработчика
  if (!CSS.highlights) {
    article.textContent = "CSS Custom Highlight API not supported.";
    return;
  }


  // Очищаем реестр выделений, чтобы удалить предыдущие результаты поиска
  CSS.highlights.clear();


  // Очищаем поисковый запрос и выходим, если он пустой
  const str = search.value.trim().toLowerCase();
  if (!str) {
    return;
  }


  // Итерируем по всем текстовым узлам и находим совпадения
  const ranges = allTextNodes
    .map((el) => {
      // Преобразуем текст текущего узла к нижнему регистру для регистронезависимого поиска
      return { el, text: el.textContent.toLowerCase() };
    })
    .map(({ text, el }) => {
      const indices = [];
      let startPos = 0;
      while (startPos < text.length) {
        // Находим индексы всех вхождений строки в тексте текущего узла
        const index = text.indexOf(str, startPos);
        if (index === -1) break;
        indices.push(index);
        startPos = index + str.length;
      }


      // Создаём объекты диапазонов для каждого найденного совпадения
      return indices.map((index) => {
        const range = new Range();
        range.setStart(el, index);
        range.setEnd(el, index + str.length);
        return range;
      });
    });


  // Создаём объект выделения для найденных диапазонов
  const searchResultsHighlight = new Highlight(...ranges.flat());


  // Регистрируем созданный объект выделения в реестре
  CSS.highlights.set("search-results", searchResultsHighlight);
});
		

Наконец, ::highlight() псевдоэлемент используется в CSS для оформления выделений:

			::highlight(search-results) {
  background-color: #f06;
  color: white;
}
		

Результат:

Стилизация участков текста с помощью CSS Custom Highlight API 2
Выделение результатов поиска

Заключение

Итак, действительно ли CSS Custom Highlight API лучший инструмент для стилизации отдельных диапазонов текста? Да, конечно.

Во-первых, даже если CSS Custom Highlight API поначалу может показаться немного сложным (необходимость создавать диапазоны, выделять и затем регистрировать их и, наконец, стилизовать), это всё равно намного проще, чем делать это без CSS Custom Highlight API. Чтобы выполнить эту задачу без этого инструмента, разработчику нужно выделять отдельные участки текста, создавать новые элементы DOM и вставлять их в нужные места. Такой способ требует больше времени у разработчика.

Что ещё более важно, браузерные движки могут стилизовать эти диапазоны очень и очень быстро. Причина, по которой с псевдоэлементом ::highlight() разрешено использовать только подмножество свойств CSS, заключается в том, что это подмножество содержит только те свойства, которые могут быть применены браузером эффективно и без необходимости воссоздавать макет страницы. Выделение диапазонов текста путём вставки новых элементов DOM на страницу вокруг них требует от движка гораздо большей работы.

Несмотря на возможности CSS Custom Highlight API, стоит обсудить и потенциальные проблемы. Одна из них — различная поддержка браузерами, что может вызвать конфликты в разработке. Кроме того, некоторые более сложные стилистические приёмы могут быть недоступны или требуют сложных решений.

Надеюсь, эта статья была полезной для вас. Спасибо за внимание.

Источники

Следите за новыми постами
Следите за новыми постами по любимым темам
587 открытий6К показов