Ленивая загрузка контента

Shepherd.js — библиотека для создания интерактивных туров по веб-приложению. Одной из ключевых возможностей является ленивая загрузка контента, которая позволяет динамически подгружать элементы интерфейса только в момент их использования в туре. Это повышает производительность и упрощает управление сложными интерфейсами.

Ленивая загрузка достигается за счёт передачи в конфигурацию шагов функции, которая возвращает содержимое только при активации шага. Вместо статического HTML или заранее подготовленных DOM-элементов, используется функция text или attachTo, которая вычисляется в момент показа шага.


Конфигурация шага с динамическим контентом

const tour = new Shepherd.Tour({
  defaultStepOptions: {
    scrollTo: true,
    classes: 'shepherd-theme-arrows'
  }
});

tour.addStep({
  id: 'step-dynamic',
  text: () => {
    const dynamicContent = document.querySelector('#dynamic-element');
    if (dynamicContent) {
      return `Содержимое элемента: ${dynamicContent.innerText}`;
    }
    return 'Элемент ещё не загружен';
  },
  attachTo: {
    element: () => document.querySelector('#dynamic-element'),
    on: 'bottom'
  }
});

tour.start();

Ключевые моменты:

  • text может быть функцией, возвращающей строку или HTML-контент.
  • attachTo.element поддерживает функцию, возвращающую DOM-элемент, что позволяет обращаться к элементам, которые появляются на странице только после асинхронной загрузки.
  • Возврат null или undefined из функции element приведёт к пропуску шага или задержке его показа до момента появления элемента.

Обработка асинхронных элементов

В реальных приложениях элементы часто загружаются через AJAX или отображаются после действий пользователя. Для корректной работы шага с ленивой загрузкой рекомендуется использовать проверку существования элемента с повторением до момента появления.

function waitForElement(selector, timeout = 3000) {
  return new Promise((resolve, reject) => {
    const interval = 50;
    let elapsed = 0;

    const check = setInterval(() => {
      const el = document.querySelector(selector);
      if (el) {
        clearInterval(check);
        resolve(el);
      }
      elapsed += interval;
      if (elapsed >= timeout) {
        clearInterval(check);
        reject(new Error('Элемент не найден'));
      }
    }, interval);
  });
}

tour.addStep({
  id: 'async-step',
  text: async () => {
    const element = await waitForElement('#async-element');
    return `Загруженный элемент содержит: ${element.innerText}`;
  },
  attachTo: {
    element: async () => await waitForElement('#async-element'),
    on: 'top'
  }
});

Особенности:

  • Асинхронные функции могут использоваться как в text, так и в attachTo.element.
  • Обработка промисов позволяет безопасно подождать появления элемента перед отображением шага.
  • Таймаут защищает от бесконечного ожидания, что важно при ошибках загрузки контента.

Интеграция с динамическими SPA

В приложениях на Vue, React или Angular компоненты часто рендерятся только после изменения состояния. Shepherd.js поддерживает динамическое получение элементов в момент показа шага, что делает библиотеку совместимой с современными SPA.

tour.addStep({
  id: 'react-step',
  text: () => {
    const componentNode = document.querySelector('.react-component');
    return componentNode ? 'Компонент готов к использованию' : 'Компонент ещё не загружен';
  },
  attachTo: {
    element: () => document.querySelector('.react-component'),
    on: 'right'
  },
  when: {
    show: () => console.log('Шаг показан')
  }
});

Рекомендации:

  • Использовать функции для element и text вместо статических селекторов.
  • При необходимости сочетать с наблюдателями за DOM (MutationObserver) для мгновенного реагирования на появление элементов.
  • Использовать опцию scrollTo: true, чтобы автоматически прокручивать страницу к лениво загруженным элементам.

Примеры расширенной ленивой загрузки

  1. Шаг с условным контентом:
tour.addStep({
  id: 'conditional-step',
  text: () => document.querySelector('#special-element') 
    ? 'Элемент доступен' 
    : 'Элемент недоступен',
  attachTo: {
    element: () => document.querySelector('#special-element'),
    on: 'bottom'
  }
});
  1. Шаг с HTML-шаблоном:
tour.addStep({
  id: 'template-step',
  text: () => `
    <h3>${document.querySelector('#title').innerText}</h3>
    <p>${document.querySelector('#description').innerText}</p>
  `,
  attachTo: {
    element: () => document.querySelector('#container'),
    on: 'left'
  }
});
  1. Комбинирование асинхронной загрузки и пользовательского события:
document.querySelector('#load-button').addEventListener('click', () => {
  tour.next();
});

tour.addStep({
  id: 'button-step',
  text: async () => {
    await waitForElement('#new-section');
    return 'Новая секция готова';
  },
  attachTo: {
    element: async () => await waitForElement('#new-section'),
    on: 'top'
  }
});

Использование ленивой загрузки в Shepherd.js позволяет строить интерактивные туры даже в самых динамичных приложениях, где элементы интерфейса появляются только при взаимодействии или после асинхронных запросов. Это снижает нагрузку на страницу и делает тур максимально адаптивным к состоянию приложения.