Контроль момента отображения шагов — ключевой аспект при работе с Shepherd.js. Библиотека сама по себе не навязывает строгую логику тайминга: разработчик вручную определяет, когда запускать тур, когда переходить к следующему шагу и на какие события реагировать.
Основные механизмы управления временем:
tour.start())next(),
back(), show())Запуск тура часто должен происходить не сразу после загрузки страницы, а после выполнения определённых условий.
setTimeout(() => {
tour.start();
}, 2000);
Используется, если интерфейс гарантированно готов через фиксированное время. Однако такой подход ненадёжен при динамической загрузке данных.
Более корректный способ — запуск после полной загрузки DOM:
document.addEventListener('DOMContentLoaded', () => {
tour.start();
});
Если приложение использует SPA-фреймворки, этого может быть недостаточно, так как элементы могут появляться позже.
Частая задача — начать тур только тогда, когда конкретный элемент доступен.
function waitForElement(selector, callback) {
const interval = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
clearInterval(interval);
callback(element);
}
}, 100);
}
waitForElement('.target', () => {
tour.start();
});
Более эффективный способ отслеживания изменений DOM:
const observer = new MutationObserver(() => {
const element = document.querySelector('.target');
if (element) {
observer.disconnect();
tour.start();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
Такой подход не нагружает процессор постоянными проверками.
По умолчанию Shepherd автоматически переключает шаги по нажатию кнопок. Однако поведение можно полностью контролировать.
tour.next();
tour.back();
tour.show('step-id');
Это позволяет:
Часто следующий шаг должен появляться только после взаимодействия пользователя.
document.querySelector('#myButton').addEventListener('click', () => {
tour.next();
});
В этом случае шаг не завершится, пока пользователь не выполнит действие.
Можно встроить обработчик прямо в шаг:
tour.addStep({
id: 'step-1',
text: 'Нажмите кнопку',
attachTo: {
element: '#myButton',
on: 'bottom'
},
when: {
show() {
const button = document.querySelector('#myButton');
button.addEventListener('click', () => {
tour.next();
});
}
}
});
Событие show срабатывает при отображении шага.
Shepherd предоставляет набор хуков:
show — шаг показанhide — шаг скрытcancel — тур отменёнcomplete — тур завершёнtour.on('show', (event) => {
console.log('Показ шага:', event.step.id);
});
Если шаг зависит от данных с сервера, необходимо дождаться завершения запроса.
fetch('/api/data')
.then(response => response.json())
.then(data => {
renderUI(data);
tour.start();
});
tour.addStep({
id: 'async-step',
text: 'Загрузка данных...',
when: {
show: async function () {
await loadData();
tour.next();
}
}
});
Важно: пользователь может не увидеть текст шага, если переход происходит слишком быстро.
Можно предотвратить переход, если условие не выполнено.
tour.addStep({
id: 'validation-step',
text: 'Введите данные',
buttons: [
{
text: 'Далее',
action: () => {
const value = document.querySelector('#input').value;
if (value) {
tour.next();
} else {
alert('Введите значение');
}
}
}
]
});
В SPA-приложениях элементы могут:
tour.addStep({
id: 'dynamic-step',
attachTo: {
element: '.dynamic-element',
on: 'right'
},
beforeShowPromise() {
return new Promise(resolve => {
waitForElement('.dynamic-element', resolve);
});
}
});
beforeShowPromise — ключевой инструмент
синхронизации.
Позволяет отложить показ шага до выполнения асинхронного кода.
tour.addStep({
id: 'step',
text: 'Ожидание...',
beforeShowPromise: () => {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
}
});
Применение:
Если интерфейс содержит анимации, важно дождаться их завершения.
tour.addStep({
id: 'animated-step',
beforeShowPromise: () => {
return new Promise(resolve => {
const element = document.querySelector('.animated');
element.addEventListener('transitionend', resolve, { once: true });
});
}
});
beforeShowPromise: () => {
return Promise.all([
waitForData(),
waitForAnimation()
]);
}
Можно запускать тур или шаги при:
window.addEventListener('hashchange', () => {
if (location.hash === '#dashboard') {
tour.start();
}
});
if (!localStorage.getItem('tourShown')) {
tour.start();
localStorage.setItem('tourShown', 'true');
}
Позволяет показывать тур только один раз.
Shepherd не имеет встроенной “паузы”, но можно реализовать её вручную:
let currentStepId;
tour.on('show', (event) => {
currentStepId = event.step.id;
});
// позже
tour.show(currentStepId);
Наиболее распространённые проблемы:
1. Шаг показывается до появления элемента
beforeShowPromise2. Элемент исчезает
3. Слишком быстрые переходы
4. Дублирование событий
{ once: true } в обработчикахbeforeShowPromise как основной
инструментДля крупных приложений рекомендуется:
Пример:
function startTourWhenReady() {
return Promise.all([
waitForUserData(),
waitForUIRender()
]).then(() => {
tour.start();
});
}
Онбординг:
Обучение функциям:
Подсказки:
Грамотное управление таймингом превращает тур из формальной подсказки в интерактивный инструмент обучения, который синхронизирован с поведением пользователя и состоянием интерфейса.