Ленивая подсветка синтаксиса кода с помощью highlight.js

Ленивая подсветка синтаксиса кода с помощью highlight.js
Содержание:
  1. Исходный код
  2. Описание процесса реализации ленивой подсветки синтаксиса кода
  3. Этап 1. Подсветка синтаксиса с помощью highlight.js
  4. Этап 2. Организация ленивого выполнения функции для раскраски кода
  5. Комментарии

В этой статье рассмотрим, как подключить highlight.js к сайту и настроить с помощью этого инструмента подсветку синтаксиса кода. Для реализации ленивого выполнения highlight.js воспользуемся Intersection Observer API.

Исходный код

Файлы решения для отложенной подсветки синтаксиса кода, расположенного в элементах <pre><code>...</code></pre>, находятся на Github.

Для вставки данного решения на страницу к ней необходимо подключить файлы main.css и main.js:

<link href="assets/css/main.css" rel="stylesheet">
<script defer src="/assets/js/main.js"></script>

Скрипт «main.js» в свою очередь добавляет в документ ещё два файла «highlight.min.css» и «highlight.min.js». Настроить их расположение можно посредством редактирования следующих строк в «main.js»:

style.href = 'assets/css/highlight.min.css';
// ...
script.src = 'assets/js/highlight.min.js';

Эти файлы загружены со страницы: https://highlightjs.org/download/

Инструмент highlight.js для подсветки синтаксиса кода на сайте

Пример подсветки JavaScript кода с помощью «highlight.js»:

<pre><code class="lang-js">function sum(a, b) {
  return a + b;
}
console.log(sum(5, 7));</code></pre>
pre code без подсветки кода и с подсветкой кода

Демо

Описание процесса реализации ленивой подсветки синтаксиса кода

Задачу по ленивой подсветки синтаксиса кода, содержащегося в <pre><code>...</code></pre>, выполним с помощью «highlight.js» и Intersection Observer API.

Для этого сначала напишем две функции:

  • loadCSSandJS() – для динамического подключения «highlight.js» к странице;
  • highlight() – для подсветки контента элементов, находящихся в массиве elements.

После этого напишем код с использованием Intersection Observer API, который будет запускать уже созданные функции, когда <pre><code> будет приближаться к области просмотра.

Этап 1. Подсветка синтаксиса с помощью highlight.js

Подсветку синтаксиса кода реализуем с помощью highlight.js.

«highlight.js» - это инструмент, который позволяет выполнять раскраску кода внутри тегов <pre><code>. Он может это делать и в браузере, и на сервере. Если язык явно не указать, то он попытается его определить автоматически.

Ручное указание языка осуществляется с помощью добавления класса:

<pre><code class="hljs language-html">
  ...
</code></pre>

Для использования «highlight.js» на веб-странице необходимо подключить стили и скрипт.

Обычно это осуществляется так:

<link rel="stylesheet" href="assets/css/highlight.min.css">
<script src="/assets/js/highlight.min.js"></script>

Но мы будем это делать динамически. Для этого напишем следующую функцию, которую будем вызывать когда <pre><code> будет находиться на расстоянии 200px до области просмотра (viewport).

// переменная, которая говорит о том подключен ли «highlight.js» к странице
let hasLoaded = false;
// функция, в которой напишем код для подсветки синтаксиса, используя методы highlight.js
const highlight = () => { ... }
// функция для динамического подключения highlight.js к странице
const loadCSSandJS = () => {
  // вставляем стили
  const style = document.createElement('link');
  style.rel = 'stylesheet';
  style.href = 'assets/css/highlight.min.css';
  document.head.appendChild(style);
  // вставляем скрипт
  const script = document.createElement('script');
  script.src = 'assets/js/highlight.min.js';
  script.async = 1;
  document.head.appendChild(script);
  script.onload = () => {
    // после загрузки скрипта присваиваем переменной hasLoaded значение true и вызываем функцию highlight для подсветки синтаксиса
    hasLoaded = true;
    highlight();
  }
}

Для подсветки синтаксиса воспользуемся методами hljs.

Когда язык установлен, то будем вызывать метод highlight:

// item - элемент (<pre><code>)
// lang - язык
hljs.highlight(item.textContent, { language: lang })

В противном случае highlightAuto:

// item - элемент (<pre><code>)
hljs.highlightAuto(item.textContent)

Реализуем функцию highlight следующим образом:

// элементы, контент которых нужно подсветить
const elements = [];
// функция highlight
const highlight = () => {
  // скопируем elements в переменную items
  const items = elements.slice();
  // очистим массив elements
  elements.length = 0;
  // переберём каждый <pre><code>
  items.forEach(item => {
    // определим, имеется ли в элементе класс lang-*
    let lang = false;
    item.classList.forEach(className => {
      if (className.indexOf('lang-') === 0) {
        lang = className.replace('lang-', '');
      }
    });
    // если класс имеется, то вызовем highlightAuto, иначе highlight
    const result = lang ? hljs.highlight(item.textContent, { language: lang }) : hljs.highlightAuto(item.textContent);
    // установим в качестве содержимого элемента HTML код, возвращённый highlight.js
    item.innerHTML = result.value;
  });
}

Этап 2. Организация ленивого выполнения функции для раскраски кода

Подсветку синтаксиса кода будем выполнять не сразу после загрузки документа, а только в тот момент, когда элемент <pre><code> будет приближаться к области просмотра.

Использование Intersection Observer API для ленивой подсветки синтаксиса кода с помощью highlight.js

Наблюдать за изменением видимости <pre><code> будем с помощью Intersection Observer.

Чтобы это сделать, необходимо создать объект Intersection Observer:

const observer = new IntersectionObserver(cb, params);

Конструктору IntersectionObserver в круглых передадим 2 аргумента:

  • функцию обратного вызова cb, которая будет выполняться всякий раз, когда отслеживаемые <pre><code> будут пересекать viewport;
  • настройки отслеживания params.

Функция обратного вызова cb:

// entries - массив объектов, каждый из которых представляет один превышенный порог, который становится более или менее видимым, чем процент, указанный этим порогом.
const cb = (entries, observer) => { ... }

Настройки отслеживания:

// root используется для указания корневого объекта (области просмотра); для установления в качестве него viewport, нужно присвоить этому ключу значение null
// rootMargin позволяет определить поля для root; зададим «0px 0px -200px 0px», чтобы пересечение считалось ниже области просмотра на 200px.
const params = {
  root: null,
  rootMargin: '0px 0px 200px 0px'
}

Добавим элементы, за которыми необходимо наблюдать в observer. Это осуществляется с помощью метода observe:

document.querySelectorAll('pre>code').forEach(item => {
  observer.observe(item);
});

Функцию обратного вызова реализуем следующим образом:

// entries - массив объектов IntersectionObserverEntry, каждый из которых представляет один целевой элемент
// observer - объект Intersection Observer, для которого вызывается функция обратного вызова
const cb = (entries, observer) => {
  // переберём все IntersectionObserverEntry
  entries.forEach(entry => {
    // если цель пересекла root
    if (entry.isIntersecting) {
      // получим целевой элемент
      const target = entry.target;
      // добавим целевой элемент в массив elements
      elements.push(target);
      // прекращаем наблюдение за этим целевым элементом
      observer.unobserve(target);
    }
  });
  // если в массиве elements имеются элементы
  if (elements.length) {
    // если highlight.js подключен к странице
    if (hasLoaded) {
      // то выполним подсветку контента элементов, вызвав для этого функцию highlight
      highlight();
    } else {
      // иначе подключим highlight.js к странице и выполним подсветку элементов, находящихся в массиве elements
      loadCSSandJS();
    }
  }
}

В ней мы пробежимся по целевым элементам и добавим в массив elements те из них, которые пересекли область просмотра. Далее выполним их раскраску.

Комментарии: 0