Intro.js предоставляет удобный механизм создания интерактивных туров по интерфейсу, но при работе с современными SPA (Single Page Application) с роутингом возникает необходимость управлять переходами между страницами или компонентами во время тура. Для этого необходимо правильно интегрировать библиотеку с системой роутинга.
Отложенный запуск шагов В SPA переход между маршрутами может происходить асинхронно. Если шаг тура ссылается на элемент, который ещё не был отрендерен, Intro.js выдаст ошибку. Решение — запускать шаги после полной загрузки целевого компонента. Это достигается через события роутера или промисы.
Привязка шагов к маршрутам Каждый шаг тура можно ассоциировать с конкретным маршрутом. Структура шага включает ключевые параметры:
const steps = [
{
element: '#step1',
intro: 'Это первый элемент на главной странице',
route: '/' // маршрут, где элемент присутствует
},
{
element: '#step2',
intro: 'Это кнопка настроек',
route: '/settings'
}
];
В данном случае свойство route — пользовательское поле,
которое поможет определить, нужно ли менять маршрут перед отображением
шага.
Для корректной работы тура с несколькими страницами необходимо внедрить логику, которая:
Пример интеграции с Vue Router:
import introJs from 'intro.js';
import router from './router';
let currentStep = 0;
function startTour(steps) {
const intro = introJs();
function showStep(stepIndex) {
const step = steps[stepIndex];
if (router.currentRoute.value.path !== step.route) {
router.push(step.route).then(() => {
// Ждем рендеринга компонента
setTimeout(() => intro.setOptions({ steps: [step] }).start(), 300);
});
} else {
intro.setOptions({ steps: [step] }).start();
}
}
intro.oncomplete(() => console.log('Тур завершен'));
intro.onexit(() => console.log('Тур прерван'));
showStep(currentStep);
}
Ключевой момент — использование отложенного вызова
setTimeout или реактивного отслеживания рендера для
уверенности, что элемент уже существует в DOM.
Для туров, которые проходят через несколько страниц, можно
использовать события onbeforechange и
onafterchange Intro.js:
intro.onbeforechange(function(targetElement) {
const stepIndex = intro._currentStep;
const step = steps[stepIndex];
if (router.currentRoute.value.path !== step.route) {
intro.exit(); // Прерываем тур
router.push(step.route).then(() => {
setTimeout(() => startTourFromStep(stepIndex), 300);
});
}
});
function startTourFromStep(index) {
currentStep = index;
startTour(steps);
}
Такой подход позволяет плавно перемещаться между страницами, сохраняя контекст тура.
Если элементы для тура загружаются динамически, необходимо убедиться, что Intro.js не пытается показать шаг до их появления. Возможные решения:
MutationObserver для отслеживания
появления элемента:function waitForElement(selector) {
return new Promise(resolve => {
const el = document.querySelector(selector);
if (el) return resolve(el);
const observer = new MutationObserver(() => {
const el = document.querySelector(selector);
if (el) {
observer.disconnect();
resolve(el);
}
});
observer.observe(document.body, { childList: true, subtree: true });
});
}
router.push('/settings').then(() => {
waitForElement('#step2').then(() => {
introJs().setOptions({ steps: [steps[1]] }).start();
});
});
Для туров через маршруты полезно хранить текущий шаг в
глобальном состоянии (Vuex, Pinia, Redux) или в
localStorage:
localStorage.setItem('tourStep', currentStep);
// При повторном открытии страницы
const savedStep = parseInt(localStorage.getItem('tourStep'), 10) || 0;
startTourFromStep(savedStep);
Это позволяет прерывать и возобновлять тур на любом шаге, независимо от переходов между страницами.
Такой подход обеспечивает плавную интеграцию Intro.js с роутером, позволяет создавать комплексные многостраничные туры и поддерживать корректное отображение интерактивных шагов даже при динамически загружаемом контенте.