В основе работы Shepherd.js лежит объект тура (Tour),
который инкапсулирует текущее состояние прохождения: активный шаг,
список шагов, настройки поведения и обработчики событий. Управление
состоянием происходит как через API самого тура, так и через встроенную
систему событий.
Создание экземпляра:
const tour = new Shepherd.Tour({
defaultStepOptions: {
cancelIcon: { enabled: true },
scrollTo: true
}
});
Внутри этого объекта поддерживается:
Получение текущего шага:
const currentStep = tour.getCurrentStep();
Проверка активности тура:
const isActive = tour.isActive();
Активный шаг — ключевой элемент состояния. Он изменяется при:
tour.next()tour.back()tour.show(id)complete) или отмене
(cancel)tour.next();
tour.back();
tour.show('step-id');
Каждый переход обновляет внутреннее состояние:
Состояние тура может изменяться динамически.
tour.addStep({
id: 'dynamic-step',
text: 'Динамически добавленный шаг',
attachTo: { element: '.dynamic', on: 'bottom' }
});
tour.removeStep('dynamic-step');
Важно: если удаляется текущий шаг, Shepherd автоматически пересчитывает состояние и может перейти к следующему доступному шагу.
tour.start();
При запуске:
tour.complete();
tour.cancel();
Разница:
complete() — нормальное завершениеcancel() — прерывание (например, пользователь закрыл
тур)После любого из этих вызовов:
Shepherd предоставляет событийную модель, позволяющую отслеживать изменения состояния.
tour.on('start', () => {});
tour.on('complete', () => {});
tour.on('cancel', () => {});
tour.on('show', (event) => {});
tour.on('hide', (event) => {});
tour.on('show', (event) => {
console.log(event.step.id);
});
События позволяют:
Для сложных приложений состояние тура часто выносится во внешний стор.
tour.on('show', (event) => {
store.dispatch({
type: 'SET_TOUR_STEP',
payload: event.step.id
});
});
const savedStep = store.getState().tourStep;
if (savedStep) {
tour.show(savedStep);
}
Такой подход позволяет:
Часто требуется изменять поведение тура в зависимости от условий.
tour.addStep({
id: 'conditional',
text: 'Шаг с условием',
when: {
show: () => {
if (!user.isAdmin) {
tour.next();
}
}
}
});
buttons: [
{
text: 'Далее',
action() {
if (form.isValid()) {
return this.next();
}
}
}
]
Shepherd поддерживает асинхронные операции перед отображением шага.
tour.addStep({
id: 'async-step',
text: 'Загрузка данных...',
beforeShowPromise() {
return fetch('/api/data')
.then(res => res.json())
.then(data => {
// обновление состояния
});
}
});
Шаг не будет показан, пока Promise не завершится.
Это позволяет:
Каждый шаг может зависеть от наличия элемента:
attachTo: {
element: '.selector',
on: 'bottom'
}
Если элемент отсутствует:
Решение — контроль состояния DOM:
beforeShowPromise() {
return new Promise(resolve => {
const interval = setInterval(() => {
if (document.querySelector('.selector')) {
clearInterval(interval);
resolve();
}
}, 100);
});
}
Кнопки шагов также отражают текущее состояние:
buttons: [
{
text: 'Назад',
action: tour.back,
secondary: true
},
{
text: 'Далее',
action: tour.next
}
]
Динамическое изменение:
step.updateStepOptions({
buttons: [...]
});
Shepherd хранит ссылку на текущий активный тур:
Shepherd.activeTour
Использование:
if (Shepherd.activeTour) {
Shepherd.activeTour.cancel();
}
Это полезно для:
При наличии нескольких туров:
const tours = {
onboarding: new Shepherd.Tour(),
advanced: new Shepherd.Tour()
};
Контроль состояния:
function startTour(name) {
if (Shepherd.activeTour) {
Shepherd.activeTour.cancel();
}
tours[name].start();
}
Для сохранения состояния:
localStorage.setItem('tour-step', tour.getCurrentStep().id);
Восстановление:
const stepId = localStorage.getItem('tour-step');
if (stepId) {
tour.start();
tour.show(stepId);
}
1. Потеря состояния при перерендере UI
Решение: хранение состояния вне компонента.
2. Попытка показать шаг до появления элемента
Решение: beforeShowPromise.
3. Конфликт нескольких туров
Решение: контроль через Shepherd.activeTour.
4. Некорректная последовательность шагов
Решение: явное управление show(id).
const tour = new Shepherd.Tour();
tour.addStep({
id: 'step-1',
text: 'Первый шаг',
buttons: [
{
text: 'Далее',
action: tour.next
}
]
});
tour.addStep({
id: 'step-2',
text: 'Второй шаг',
buttons: [
{
text: 'Назад',
action: tour.back
},
{
text: 'Завершить',
action: tour.complete
}
]
});
tour.on('show', (e) => {
console.log('Текущий шаг:', e.step.id);
});
tour.start();
Этот пример демонстрирует полный цикл управления состоянием: