Intro.js — это библиотека для создания интерактивных пошаговых руководств по интерфейсу веб-приложения. Одним из ключевых аспектов продвинутого использования является интеграция с серверным хранилищем для сохранения прогресса пользователя, настройки последовательности шагов и динамической генерации контента.
Для того чтобы пользователь мог продолжить обучение с того места, где он остановился, необходимо сохранять текущий шаг на сервере. Для этого используется AJAX-запрос к серверному API. Пример:
function saveUserProgress(userId, step) {
fetch('/api/saveProgress', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ userId, step })
})
.then(response => response.json())
.then(data => {
console.log('Прогресс сохранён:', data);
})
.catch(error => console.error('Ошибка при сохранении прогресса:', error));
}
В этом примере сервер получает userId и текущий шаг
step и сохраняет их в базе данных. На стороне клиента можно
использовать эту функцию в событии onchange:
introJs().onchange(function(targetElement) {
const step = this._currentStep;
saveUserProgress(currentUserId, step);
});
Часто шаги руководства динамически формируются в зависимости от данных пользователя. Для этого список шагов можно получать с сервера в формате JSON:
fetch(`/api/getIntroSteps?userId=${currentUserId}`)
.then(res => res.json())
.then(steps => {
introJs().setOptions({ steps }).start();
});
Пример структуры данных для шагов:
[
{
"element": "#button1",
"intro": "Это основная кнопка для действий",
"position": "right"
},
{
"element": "#menu",
"intro": "Здесь находится главное меню",
"position": "bottom"
}
]
Использование серверного API позволяет создавать персонализированные инструкции, например скрывать шаги, которые пользователь уже выполнил.
При работе с серверным хранилищем необходимо учитывать безопасность:
Пример передачи токена:
fetch('/api/saveProgress', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userToken}`
},
body: JSON.stringify({ userId, step })
});
С помощью сервера можно динамически менять порядок шагов в зависимости от действий пользователя или версии интерфейса. Например, если пользователь пропустил определённые функции, сервер может добавить дополнительные шаги:
fetch(`/api/getDynamicSteps?userId=${currentUserId}`)
.then(res => res.json())
.then(steps => {
const intro = introJs();
intro.setOptions({ steps });
intro.start();
});
Такой подход полезен для сложных интерфейсов, где последовательность шагов может зависеть от роли пользователя, предыдущего прогресса или состояния данных.
Intro.js предоставляет несколько событий, которые удобно использовать для синхронизации с сервером:
onchange(targetElement) — вызывается при смене шага,
можно сохранять прогресс.onexit() — вызывается при завершении руководства, можно
отправить финальный статус.onbeforechange(targetElement) — позволяет предотвратить
переход к следующему шагу, например, если сервер возвращает ошибку.Пример использования onexit:
introJs().onexit(function() {
fetch('/api/finishIntro', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId: currentUserId })
});
});
При частых обращениях к серверу имеет смысл использовать
локальное кэширование прогресса с помощью
localStorage или IndexedDB. Это позволяет:
Пример:
function saveProgressLocally(step) {
localStorage.setItem('introStep', step);
}
function syncProgressWithServer(userId) {
const step = localStorage.getItem('introStep');
if (step) {
saveUserProgress(userId, step);
}
}
При интеграции с сервером необходимо учитывать ошибки сети и возможность повторных попыток:
async function saveProgressWithRetry(userId, step, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await fetch('/api/saveProgress', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, step })
});
return;
} catch (e) {
if (i === retries - 1) console.error('Не удалось сохранить прогресс:', e);
}
}
}
На серверной стороне прогресс и шаги обычно хранятся в базе данных. Типичная структура таблиц:
Таблица intro_steps:
| id | element | intro | position |
|---|
Таблица user_intro_progress:
Такое разделение позволяет: