Shepherd.js — это библиотека для создания интерактивных туров по веб-приложениям. Основной механизм библиотеки строится вокруг шагов (steps), каждый из которых привязан к DOM-элементу или позиционируется относительно окна. Каждый шаг может содержать текстовые подсказки, кнопки управления и кастомные обработчики событий.
При динамическом обновлении интерфейса возникает проблема частых перерисовок, которые могут замедлять работу и вызывать визуальные сбои. Shepherd.js, как правило, инициирует рендер каждого шага при его открытии, пересчитывая позицию относительно целевого элемента. Минимизация этих пересчетов позволяет оптимизировать производительность и улучшить UX.
Каждый шаг создается через метод tour.addStep() с
указанием параметров:
const tour = new Shepherd.Tour({
defaultStepOptions: {
scrollTo: true,
cancelIcon: { enabled: true }
}
});
tour.addStep({
id: 'example-step',
text: 'Пример шага',
attachTo: { element: '#my-element', on: 'bottom' },
buttons: [
{
text: 'Далее',
action: tour.next
}
]
});
Ключевой момент: каждый шаг вычисляет свою позицию
относительно целевого элемента при вызове метода show().
Если DOM изменяется, библиотека может инициировать повторные расчеты.
Это становится критичным при сложных интерфейсах с анимациями и
динамическим контентом.
scrollToShepherd.js по умолчанию прокручивает страницу до элемента. Опция
scrollTo может вызвать дополнительные
перерисовки:
defaultStepOptions: {
scrollTo: false
}
Установка scrollTo: false исключает автоматическую
прокрутку, что снижает количество пересчетов позиции шага и ускоряет
отображение. При необходимости прокрутка может быть реализована вручную
с помощью методов браузера или библиотек вроде
IntersectionObserver.
Частая ошибка — каждый шаг заново ищет элемент через селектор
document.querySelector(). При динамическом интерфейсе это
приводит к множественным обращениям к DOM. Оптимальная стратегия:
const targetElement = document.getElementById('my-element');
tour.addStep({
id: 'step-cached',
text: 'Шаг с кэшированным элементом',
attachTo: { element: targetElement, on: 'top' }
});
Преимущество: позиция шага вычисляется только один раз, если элемент остается в DOM, снижая нагрузку на рендер.
Shepherd.js предоставляет метод updateStepOptions() для
изменения позиции или текста шага без полной перерисовки:
const step = tour.getById('example-step');
step.updateStepOptions({
attachTo: { element: '#new-element', on: 'right' }
});
Важно помнить: вызов show() после обновления инициирует
пересчет координат. Чтобы минимизировать лишние
пересчеты, следует группировать все изменения до одного вызова
show().
Для приложений с часто меняющимся контентом полезно использовать дебаунс обновлений:
let updateTimeout;
function updateStepPositionDebounced(step) {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(() => {
step.show();
}, 100); // задержка в 100 мс
}
Это предотвращает множественные вызовы show() при
анимациях или изменении размеров элементов, минимизируя перерисовки.
Shepherd.js позволяет использовать when для привязки к
событиям:
tour.addStep({
id: 'step-event',
text: 'Ожидаем событие',
when: {
show: () => console.log('Шаг отображен')
}
});
Можно использовать собственные события для обновления только при необходимости, например, когда элемент реально появился в DOM:
document.addEventListener('dynamicContentLoaded', () => {
step.updateStepOptions({ attachTo: { element: '#dynamic-element', on: 'left' } });
});
Такой подход предотвращает ненужные пересчеты позиций при каждом рендере интерфейса.
Для многошаговых туров с динамическими изменениями интерфейса рекомендуются следующие практики:
scrollTo: false) при ненужной прокрутке.show().Эти методы обеспечивают плавное отображение туров, уменьшение нагрузки на DOM и предотвращение «дрожания» подсказок при динамических интерфейсах.
const tour = new Shepherd.Tour({ defaultStepOptions: { scrollTo: false } });
const cachedButton = document.querySelector('#start-btn');
tour.addStep({
id: 'start',
text: 'Начало тура',
attachTo: { element: cachedButton, on: 'bottom' },
buttons: [{ text: 'Далее', action: tour.next }]
});
const dynamicStep = tour.addStep({
id: 'dynamic',
text: 'Шаг с динамическим элементом',
attachTo: { element: null, on: 'top' }
});
document.addEventListener('dynamicLoaded', () => {
const dynamicElement = document.querySelector('#dynamic-element');
dynamicStep.updateStepOptions({ attachTo: { element: dynamicElement, on: 'top' } });
});
В этом примере кэширование, дебаунс и отложенное обновление позволяют уменьшить количество пересчетов позиции, сохраняя интерактивность и скорость отображения шагов.