Работа с SPA

Driver.js — это библиотека для создания интерактивных пошаговых подсказок на веб-страницах. Для использования в SPA-приложениях сначала необходимо подключить библиотеку.

Через npm:

npm install driver.js

Импорт в модульном проекте:

import Driver from 'driver.js';
import 'driver.js/dist/driver.min.css';

Если проект не использует сборщики модулей, можно подключить через CDN:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js/dist/driver.min.css" />
<script src="https://cdn.jsdelivr.net/npm/driver.js/dist/driver.min.js"></script>

После подключения создается экземпляр Driver:

const driver = new Driver({
    animate: true,
    opacity: 0.75,
    padding: 10,
    allowClose: false,
    doneBtnText: 'Готово',
    closeBtnText: 'Закрыть'
});

Основные методы и конфигурация

defineSteps(steps) — задает последовательность шагов для подсказки. Каждый шаг представляет объект:

driver.defineSteps([
  {
    element: '#menu',
    popover: {
      title: 'Меню',
      description: 'Здесь отображается навигация по приложению.',
      position: 'bottom'
    }
  },
  {
    element: '#search',
    popover: {
      title: 'Поиск',
      description: 'Поиск по всем разделам SPA.',
      position: 'right'
    }
  }
]);

Ключевые свойства шага:

  • element — селектор или DOM-узел для подсветки.
  • popover.title — заголовок всплывающей подсказки.
  • popover.description — описание функционала.
  • popover.position — позиция подсказки относительно элемента (top, bottom, left, right, center).
  • popover.align — выравнивание внутри позиции (start, center, end).
  • onNext, onPrevious, onHighlightStarted, onHighlightEnded — колбэки для управления поведением шагов.

start() — запускает последовательность шагов. reset() — сбрасывает состояние Driver.js. moveNext() / movePrevious() — переход между шагами вручную.

Интеграция с SPA

В SPA-приложениях DOM-элементы часто рендерятся динамически, поэтому ключевым моментом является отложенное определение шагов после рендера нужного элемента.

Пример с React:

useEffect(() => {
  if (document.querySelector('#profile-button')) {
    driver.defineSteps([
      {
        element: '#profile-button',
        popover: {
          title: 'Профиль',
          description: 'Управление вашим профилем',
          position: 'left'
        }
      }
    ]);
    driver.start();
  }
}, [currentPage]);

Важно убедиться, что элемент существует в DOM перед созданием шага. Если элемент появится позже, можно использовать MutationObserver:

const observer = new MutationObserver(() => {
  const el = document.querySelector('#dynamic-element');
  if (el) {
    driver.defineSteps([
      { element: '#dynamic-element', popover: { title: 'Динамический', description: 'Элемент загружен', position: 'bottom' } }
    ]);
    driver.start();
    observer.disconnect();
  }
});
observer.observe(document.body, { childList: true, subtree: true });

Динамическая подгрузка контента

Для страниц, где контент загружается асинхронно (например, через fetch или GraphQL), необходимо:

  1. Ждать завершения рендера элемента.
  2. Вызывать defineSteps и start после появления элемента.
  3. При переходе на другой маршрут SPA — сбрасывать старые шаги с помощью reset().

Пример для Vue:

watch(() => route.name, () => {
  driver.reset();
  nextTick(() => {
    const el = document.querySelector('#new-section');
    if (el) {
      driver.defineSteps([
        { element: '#new-section', popover: { title: 'Новый раздел', description: 'Описание раздела', position: 'top' } }
      ]);
      driver.start();
    }
  });
});

Расширенные возможности

Скрытие подсказки по условию: можно добавлять проверку на существование элемента и пропускать шаг:

driver.defineSteps([
  {
    element: '#optional',
    popover: { title: 'Опциональный элемент', description: 'Может отсутствовать' },
    onBeforeStep: (step) => {
      return !!document.querySelector(step.element);
    }
  }
]);

Стилизация: Driver.js поддерживает кастомные CSS классы для popover:

driver.defineSteps([
  {
    element: '#styled',
    popover: { title: 'Кастомный стиль', description: 'Используем классы', position: 'bottom', className: 'my-driver-style' }
  }
]);

Работа с состояниями маршрутов

Для SPA важно, чтобы подсказки корректно реагировали на изменение маршрута. В большинстве случаев удобно привязывать шаги к событию изменения маршрута:

router.afterEach((to) => {
  driver.reset();
  nextTick(() => {
    if (to.name === 'Dashboard') {
      driver.defineSteps([
        { element: '#dashboard-widget', popover: { title: 'Виджет', description: 'Основная информация', position: 'right' } }
      ]);
      driver.start();
    }
  });
});

Так обеспечивается корректное управление подсказками на динамически изменяющемся DOM без ошибок element not found.

Заключение по практическим приёмам

  • Всегда проверять наличие элементов перед созданием шага.
  • Для асинхронного контента использовать MutationObserver или хуки жизненного цикла фреймворка.
  • Сбрасывать состояние Driver.js при навигации между маршрутами SPA.
  • Использовать колбэки (onNext, onPrevious) для динамического изменения шагов в процессе прохождения тура.
  • Кастомизировать popover через CSS или классы для согласованного визуального стиля.

Эти подходы позволяют создавать интерактивные и надежные пользовательские туры даже в сложных одностраничных приложениях.