Позиционирование относительно элемента

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

Ключевую роль играет свойство attachTo, которое определяет:

  • элемент, к которому происходит привязка;
  • сторону, с которой отображается подсказка.
{
  attachTo: {
    element: '.button-start',
    on: 'bottom'
  }
}
  • element — CSS-селектор или DOM-узел;
  • on — положение тултипа относительно элемента.

Допустимые значения позиции

Свойство on поддерживает набор направлений, определяющих расположение:

Значение Описание
top над элементом
bottom под элементом
left слева
right справа
top-start сверху, выравнивание по левому краю
top-end сверху, выравнивание по правому краю
bottom-start снизу, выравнивание по левому краю
bottom-end снизу, выравнивание по правому краю

Пример:

attachTo: {
  element: '#menu',
  on: 'right-start'
}

Такое позиционирование размещает шаг справа от элемента с выравниванием по верхнему краю.


Автоматическая адаптация позиции

Shepherd.js использует библиотеку позиционирования (Popper.js), которая автоматически корректирует положение, если заданное направление невозможно (например, элемент находится у края экрана).

Поведение включает:

  • переворот (flip) — смена стороны (например, bottomtop);
  • сдвиг (shift) — корректировка внутри доступной области;
  • ограничение по viewport.

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


Отсутствие привязки к элементу

Если attachTo не задан, шаг отображается по центру экрана:

{
  text: 'Общий шаг без привязки'
}

Такой подход используется для:

  • вводных экранов;
  • общих инструкций;
  • финальных сообщений.

Динамическое указание элемента

Вместо статического селектора допускается использование функции:

attachTo: {
  element: () => document.querySelector('.dynamic'),
  on: 'bottom'
}

Преимущества:

  • поддержка динамически создаваемых элементов;
  • корректная работа с асинхронной загрузкой интерфейса;
  • гибкость при изменении DOM.

Поведение при отсутствии элемента

Если элемент не найден:

  • шаг не будет привязан;
  • возможны ошибки позиционирования;
  • тур может остановиться или перейти к следующему шагу (в зависимости от конфигурации).

Практика обработки:

attachTo: {
  element: () => document.querySelector('.optional') || 'body',
  on: 'bottom'
}

Смещение (offset)

Для точной настройки положения используется смещение через настройки Popper:

popperOptions: {
  modifiers: [
    {
      name: 'offset',
      options: {
        offset: [0, 10]
      }
    }
  ]
}
  • первый параметр — смещение по основной оси;
  • второй — по перпендикулярной.

Пример:

  • [0, 10] — сдвиг вниз;
  • [10, 0] — сдвиг вправо.

Ограничение области позиционирования

Можно ограничить область, внутри которой будет располагаться шаг:

popperOptions: {
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        boundary: document.body
      }
    }
  ]
}

Это важно для:

  • модальных окон;
  • контейнеров с overflow;
  • сложных layout’ов.

Работа с прокруткой

Если элемент находится вне видимой области, Shepherd автоматически прокручивает страницу.

Настройка:

scrollTo: true

Или более детально:

scrollTo: {
  beh * avior: 'smooth',
  block: 'center'
}

Параметры:

  • behavior: auto или smooth;
  • block: start, center, end.

Подсветка элемента

При позиционировании часто используется выделение элемента:

highlightClass: 'highlighted-element'

Shepherd добавляет CSS-класс к целевому элементу, позволяя:

  • визуально акцентировать внимание;
  • затемнить остальную часть интерфейса.

Работа с несколькими элементами

Иногда требуется привязка к одному из нескольких элементов:

attachTo: {
  element: () => document.querySelectorAll('.item')[0],
  on: 'top'
}

Или с логикой выбора:

attachTo: {
  element: () => {
    const items = document.querySelectorAll('.item');
    return items.length ? items[items.length - 1] : null;
  },
  on: 'bottom'
}

Использование пользовательских контейнеров

Shepherd позволяет рендерить шаги внутри определённого контейнера:

stepsContainer: document.querySelector('#tour-container')

Это влияет на:

  • контекст позиционирования;
  • z-index;
  • взаимодействие с layout.

Особенности позиционирования внутри flex и grid

При работе с современными layout-системами:

  • элементы могут менять положение динамически;
  • размеры могут зависеть от контента.

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

  • использовать динамические функции в attachTo;
  • избегать жёсткой привязки к нестабильным элементам;
  • учитывать reflow при изменениях DOM.

Позиционирование при изменении размеров окна

Shepherd автоматически пересчитывает положение при:

  • ресайзе окна;
  • изменении ориентации устройства;
  • изменении размеров элемента.

Дополнительно можно вручную инициировать обновление:

step.updateStepOptions({});

Привязка к SVG и нестандартным элементам

Shepherd корректно работает с:

  • SVG-элементами;
  • canvas (через обёртки);
  • кастомными компонентами.

Важно:

  • элемент должен быть доступен через DOM;
  • должен иметь корректные размеры (не display: none).

Ошибки позиционирования и их причины

Наиболее распространённые проблемы:

1. Элемент скрыт

display: none;

→ позиционирование невозможно.

2. Неправильный селектор → элемент не найден.

3. Нулевые размеры → Popper не может вычислить позицию.

4. Контейнер с overflow

overflow: hidden;

→ обрезание тултипа.


Лучшие практики

  • Использование стабильных селекторов (data-* атрибуты)
  • Проверка существования элементов перед запуском тура
  • Минимизация жёстких смещений
  • Учет адаптивности интерфейса
  • Тестирование на разных разрешениях

Пример комплексной настройки шага

tour.addStep({
  id: 'example',
  text: 'Описание элемента',
  attachTo: {
    element: () => document.querySelector('.target'),
    on: 'bottom-start'
  },
  scrollTo: {
    beh * avior: 'smooth',
    block: 'center'
  },
  popperOptions: {
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 12]
        }
      },
      {
        name: 'preventOverflow',
        options: {
          boundary: document.body
        }
      }
    ]
  },
  highlightClass: 'active-highlight'
});

Такой подход обеспечивает:

  • точное позиционирование;
  • устойчивость к изменениям интерфейса;
  • корректную работу в сложных layout’ах.