В интерфейсах с пошаговыми подсказками важно учитывать, что пользователь может прервать обучение, обновить страницу или вернуться позже. Без механизма сохранения состояния Intro.js каждый запуск тура будет начинаться заново, что ухудшает пользовательский опыт.
Сохранение прогресса позволяет:
Intro.js не предоставляет встроенного механизма сохранения прогресса, поэтому решение строится на внешнем хранилище. Основные варианты:
Чаще всего используется localStorage, так как он прост и
не требует серверной логики.
Intro.js предоставляет события, позволяющие отслеживать переходы
между шагами. Основное — onchange.
Пример сохранения текущего шага:
const intro = introJs();
intro.onchange(function (targetElement) {
const step = intro._currentStep;
localStorage.setItem('introStep', step);
});
intro.start();
Здесь:
intro._currentStep — текущий индекс шагаПри повторной инициализации тура необходимо извлечь сохранённый шаг и установить его.
const savedStep = parseInt(localStorage.getItem('introStep'), 10);
const intro = introJs();
if (!isNaN(savedStep)) {
intro.goToStep(savedStep + 1);
}
intro.start();
Особенности:
goToStep использует нумерацию с 1После полного прохождения тура важно удалить сохранённый прогресс, чтобы не запускать его повторно.
intro.oncomplete(function () {
localStorage.removeItem('introStep');
});
intro.onexit(function () {
localStorage.removeItem('introStep');
});
События:
oncomplete — пользователь прошёл тур до концаonexit — пользователь вышел вручнуюИногда достаточно не хранить шаг, а просто запомнить, что тур уже был пройден.
if (!localStorage.getItem('introCompleted')) {
introJs().start();
}
При завершении:
intro.oncomplete(function () {
localStorage.setItem('introCompleted', 'true');
});
Такой подход используется, когда повторное обучение не требуется.
В более сложных сценариях требуется учитывать:
В этом случае сохраняется не один индекс, а структура:
const progress = {
currentStep: 2,
completedSteps: [0, 1],
skippedSteps: [3]
};
localStorage.setItem('introProgress', JSON.stringify(progress));
Восстановление:
const progress = JSON.parse(localStorage.getItem('introProgress'));
if (progress) {
intro.goToStep(progress.currentStep + 1);
}
Если шаги привязаны к элементам, которые могут отсутствовать (например, из-за условий интерфейса), восстановление может ломаться.
Решения:
onbeforechange для контроляintro.onbeforechange(function (targetElement) {
if (!targetElement) {
intro.nextStep();
}
});
Для авторизованных пользователей прогресс часто сохраняется на сервере.
Пример:
intro.onchange(function () {
fetch('/api/save-progress', {
method: 'POST',
body: JSON.stringify({
step: intro._currentStep
}),
headers: {
'Content-Type': 'application/json'
}
});
});
При загрузке страницы:
fetch('/api/get-progress')
.then(res => res.json())
.then(data => {
if (data.step !== undefined) {
intro.goToStep(data.step + 1).start();
}
});
Преимущества:
При перезагрузке страницы важно:
Комбинация:
window.addEventListener('beforeunload', function () {
localStorage.setItem('introStep', intro._currentStep);
});
И при загрузке:
window.onl oad = function () {
const step = localStorage.getItem('introStep');
if (step !== null) {
introJs().goToStep(parseInt(step) + 1).start();
}
};
Иногда тур должен показываться:
Пример с таймером:
const lastShown = localStorage.getItem('introLastShown');
const now = Date.now();
if (!lastShown || now - lastShown > 7 * 24 * 60 * 60 * 1000) {
introJs().start();
localStorage.setItem('introLastShown', now);
}
Минимизировать навязчивость
Сохранять только необходимое
Учитывать изменения интерфейса
Обрабатывать ошибки
Использовать именование ключей
intro_step_main_page
intro_completed_dashboard
Это помогает избежать конфликтов при нескольких турах.
localStorage.setItem('intro_dashboard_step', step);
localStorage.setItem('intro_profile_completed', true);
Каждый тур имеет собственное состояние.
const role = 'admin';
localStorage.setItem(`intro_${role}_step`, step);
Позволяет показывать разные туры для разных типов пользователей.
const version = '1.2.0';
localStorage.setItem('introVersion', version);
Если версия изменилась — тур можно запустить заново.
if (localStorage.getItem('introVersion') !== version) {
introJs().start();
}