Работа с библиотекой Shepherd.js предполагает управление пользовательскими турами, состоящими из шагов (steps), привязанных к DOM-элементам. В процессе выполнения тура возможны различные ошибки: отсутствие целевого элемента, конфликт состояний, неправильная конфигурация шагов, асинхронные задержки загрузки интерфейса. Корректная обработка таких ситуаций позволяет избежать сбоев и улучшает пользовательский опыт.
Наиболее частая проблема — элемент, указанный в
attachTo, отсутствует в DOM в момент показа шага.
{
attachTo: {
element: '.missing-element',
on: 'bottom'
}
}
Причины:
Последствия:
Ошибки в структуре шага:
tour.addStep({
text: null,
attachTo: 'invalid-format'
});
Проблемы:
text не строка или отсутствуетattachTo задан неверноПри загрузке данных или элементов через AJAX / SPA-фреймворки:
Использование функции beforeShowPromise позволяет
отложить отображение шага до тех пор, пока элемент не появится.
tour.addStep({
attachTo: {
element: '.dynamic-element',
on: 'right'
},
beforeShowPromise: function() {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
const el = document.querySelector('.dynamic-element');
if (el) {
clearInterval(interval);
resolve();
}
}, 100);
setTimeout(() => {
clearInterval(interval);
reject('Element not found');
}, 5000);
});
}
});
Ключевые моменты:
Shepherd предоставляет события, на которые можно подписаться:
tour.on('show', (event) => {
console.log('Показ шага:', event.step.id);
});
tour.on('cancel', () => {
console.warn('Тур отменён');
});
tour.on('complete', () => {
console.log('Тур завершён');
});
Использование для обработки ошибок:
При программном управлении туром:
try {
tour.start();
} catch (error) {
console.error('Ошибка запуска тура:', error);
}
Аналогично для переходов:
try {
tour.next();
} catch (error) {
console.error('Ошибка перехода:', error);
}
Перед добавлением шагов полезно валидировать данные:
function validateStep(step) {
if (!step.text) {
throw new Error('Step must have text');
}
if (!step.attachTo || !step.attachTo.element) {
throw new Error('attachTo.element is required');
}
}
Применение:
steps.forEach(step => {
validateStep(step);
tour.addStep(step);
});
if (!tour.isActive()) {
tour.start();
}
Это предотвращает ошибки состояния и дублирование интерфейса.
Если элемент не найден, можно автоматически перейти дальше:
tour.addStep({
attachTo: {
element: '.optional-element',
on: 'bottom'
},
when: {
show() {
const el = document.querySelector('.optional-element');
if (!el) {
tour.next();
}
}
}
});
if (document.querySelector('.feature')) {
tour.addStep({
text: 'Описание функции',
attachTo: {
element: '.feature',
on: 'top'
}
});
}
function logError(message, context) {
console.error(`[Shepherd Error]: ${message}`, context);
}
Пример использования:
tour.on('cancel', () => {
logError('Tour cancelled unexpectedly', { step: tour.getCurrentStep() });
});
Возможна отправка ошибок в сторонние сервисы:
function reportError(error) {
fetch('/log', {
method: 'POST',
body: JSON.stringify({
message: error.message,
stack: error.stack
})
});
}
buttons: [
{
text: 'Далее',
action() {
try {
this.next();
} catch (e) {
console.error('Ошибка кнопки:', e);
}
}
}
]
canClickTarget: false
Снижает вероятность ошибок, связанных с некорректными кликами пользователя.
beforeShowPromise() {
return fetch('/data')
.then(res => res.json())
.then(data => {
if (!data.ready) {
throw new Error('Data not ready');
}
});
}
beforeShowPromise() {
return new Promise((resolve, reject) => {
someAsyncOperation()
.then(resolve)
.catch(err => {
console.error('Ошибка async:', err);
reject(err);
});
});
}
function waitForElement(selector, timeout = 3000) {
return new Promise((resolve, reject) => {
const start = Date.now();
const check = () => {
if (document.querySelector(selector)) {
resolve();
} else if (Date.now() - start > timeout) {
reject('Timeout');
} else {
requestAnimationFrame(check);
}
};
check();
});
}
beforeShowPromise для асинхронных
интерфейсовisActive,
getCurrentStep)| Ошибка | Причина | Решение |
|---|---|---|
| Шаг не отображается | Нет элемента | Проверка + beforeShowPromise |
| Тур зависает | Promise не завершён | Добавить тайм-аут |
| Ошибка при next() | Тур завершён | Проверка isActive() |
| Дублирование шагов | Повторный запуск | Контроль состояния |
| Неправильное позиционирование | Неверный селектор | Проверка attachTo |
Для крупных проектов обработка ошибок должна быть частью общей архитектуры:
Пример структуры:
class TourManager {
constructor() {
this.tour = new Shepherd.Tour();
}
safeStart() {
try {
if (!this.tour.isActive()) {
this.tour.start();
}
} catch (e) {
this.handleError(e);
}
}
handleError(error) {
console.error('Tour error:', error);
}
}
Такой подход обеспечивает предсказуемость поведения и упрощает масштабирование системы туров.