Структура DOM-элементов

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

DOM (Document Object Model) представляет веб-страницу в виде дерева узлов, где каждый узел — это элемент HTML, текст или атрибут. Shepherd.js использует селекторы CSS для привязки подсказок к нужным элементам, поэтому знание структуры DOM критически важно для корректной работы туров.


Определение целевых элементов

Каждый шаг тура в Shepherd.js создаётся через объект step, где ключевым свойством является attachTo. Оно определяет элемент DOM и позицию подсказки относительно него.

const tour = new Shepherd.Tour({
  defaultStepOptions: {
    scrollTo: true
  }
});

tour.addStep({
  id: 'step-1',
  text: 'Это основной заголовок страницы.',
  attachTo: {
    element: '#main-title',
    on: 'bottom'
  }
});

tour.start();

Пояснения:

  • element — CSS-селектор или DOM-элемент, к которому привязывается подсказка.
  • on — позиция подсказки относительно элемента (top, bottom, left, right).
  • scrollTo: true — автоматически прокручивает страницу, чтобы элемент был видим.

Важность структуры DOM

Эффективное использование Shepherd.js требует предсказуемой структуры DOM. Несколько рекомендаций:

  1. Уникальные идентификаторы Элементы, к которым привязываются шаги, должны иметь уникальные id. Это снижает риск конфликта селекторов и обеспечивает стабильность туров.

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

tour.addStep({
  id: 'dynamic-step',
  text: 'Этот элемент появляется после загрузки данных.',
  attachTo: { element: '.dynamic-element', on: 'right' },
  beforeShowPromise: () => new Promise(resolve => {
    const check = setInterval(() => {
      if (document.querySelector('.dynamic-element')) {
        clearInterval(check);
        resolve();
      }
    }, 100);
  })
});
  1. Контейнеры и вложенные элементы Shepherd.js корректно работает с вложенными элементами, однако важно учитывать, что позиция подсказки рассчитывается относительно ближайшего позиционированного предка. Если элемент находится в overflow: hidden контейнере, подсказка может обрезаться. В таких случаях используют useModalOverlay: true или перемещают шаг в более подходящий контейнер.

Атрибуты и классы

Помимо id, для точной привязки можно использовать классы и другие атрибуты:

tour.addStep({
  id: 'step-class',
  text: 'Привязка по классу.',
  attachTo: { element: '.feature-card[data-feature="1"]', on: 'left' }
});

Shepherd.js поддерживает все стандартные CSS-селекторы, включая nth-child, псевдоклассы и атрибутные селекторы. Это позволяет выбирать элементы даже в сложных DOM-структурах без изменения HTML.


Динамическое управление DOM

Иногда элементы создаются динамически, например, после AJAX-запросов. Shepherd.js предоставляет методы для обновления шагов:

  • tour.addStep(step) — добавить шаг после загрузки элемента.
  • tour.removeStep(id) — удалить шаг, если элемент исчез.
  • tour.next() и tour.back() — перемещение по шагам, независимо от текущего состояния DOM.

Использование beforeShowPromise в комбинации с динамическими шагами позволяет корректно отображать подсказки даже на страницах с асинхронным контентом.


Совмещение с фреймворками

При работе с React, Vue или Angular элементы DOM часто создаются компонентами и могут отсутствовать при инициализации тура. В таких случаях важно:

  1. Инициализировать Shepherd.js после рендера компонентов.
  2. Использовать колбэки для ожидания монтирования нужных элементов.
  3. Привязывать шаги к реальным DOM-узлам, а не к виртуальным компонентам.

Пример для React:

useEffect(() => {
  const tour = new Shepherd.Tour({ defaultStepOptions: { scrollTo: true } });
  tour.addStep({
    id: 'react-step',
    text: 'Подсказка для компонента.',
    attachTo: { element: document.querySelector('#react-component'), on: 'bottom' }
  });
  tour.start();
}, []);

Взаимодействие с модальными окнами

Если элемент находится внутри модального окна, важно, чтобы:

  • Модальное окно было открыто до показа шага.
  • Использовать beforeShowPromise для ожидания открытия окна.
  • Проверить, что контейнер модального окна не перекрывает подсказку.
tour.addStep({
  id: 'modal-step',
  text: 'Шаг внутри модального окна.',
  attachTo: { element: '#modal-content', on: 'top' },
  beforeShowPromise: () => openModalAndWait('#modal-content')
});

Выводы по структуре DOM

  • Каждому шагу нужен предсказуемый элемент DOM.
  • Уникальные идентификаторы и стабильная структура — ключ к успешному туру.
  • Для динамических элементов использовать beforeShowPromise.
  • Контейнеры с overflow и модальные окна требуют особого внимания.
  • CSS-селекторы позволяют гибко привязывать подсказки даже в сложной структуре.

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