Сохранение прогресса в localStorage

Intro.js — это библиотека для создания пошаговых интерактивных руководств на веб-страницах. Один из ключевых аспектов её использования — возможность сохранять прогресс пользователя, чтобы при повторном посещении сайта руководство не начиналось заново. Для этого используется браузерное хранилище localStorage, предоставляющее постоянное хранение данных на стороне клиента.


Подключение Intro.js и инициализация

Для начала подключается сама библиотека Intro.js:

<link rel="stylesheet" href="https://unpkg.com/intro.js/minified/introjs.min.css">
<script src="https://unpkg.com/intro.js/minified/intro.min.js"></script>

Инициализация стандартного руководства:

const intro = introJs();
intro.setOptions({
    steps: [
        { element: '#step1', intro: 'Это первый шаг.' },
        { element: '#step2', intro: 'Это второй шаг.' },
        { element: '#step3', intro: 'Это третий шаг.' }
    ],
    showProgress: true,
    exitOnOverlayClick: false
});

Сохранение прогресса

LocalStorage позволяет хранить данные в виде пар ключ-значение, где значение обязательно должно быть строкой. Для сохранения шага руководства используется следующий подход:

intro.onchange(function(targetElement) {
    const currentStep = intro._currentStep;
    localStorage.setItem('introjsCurrentStep', currentStep);
});
  • intro.onchange — событие, вызываемое при переходе на новый шаг.
  • intro._currentStep — внутреннее свойство Intro.js, указывающее текущий шаг.
  • localStorage.setItem — сохраняет номер шага в локальном хранилище браузера.

Восстановление прогресса при загрузке страницы

Для того чтобы продолжить руководство с того шага, на котором пользователь остановился:

const savedStep = localStorage.getItem('introjsCurrentStep');

if (savedStep !== null) {
    intro.start().goToStep(Number(savedStep + 1)).start();
} else {
    intro.start();
}
  • localStorage.getItem извлекает сохранённое значение.
  • goToStep(step) — перемещает Intro.js на указанный шаг (нумерация начинается с 1).
  • Необходимо преобразовать значение в число через Number, так как localStorage хранит только строки.

Автоматическая очистка прогресса

В некоторых случаях требуется, чтобы руководство начиналось заново после завершения или по определённому условию:

intro.oncomplete(function() {
    localStorage.removeItem('introjsCurrentStep');
});

intro.onexit(function() {
    localStorage.removeItem('introjsCurrentStep');
});
  • oncomplete срабатывает, когда пользователь прошёл все шаги.
  • onexit срабатывает, если пользователь закрыл руководство до конца.
  • localStorage.removeItem удаляет сохранённое значение, чтобы при следующем запуске шаг начинался с первого.

Сохранение состояния между сессиями

Для более сложных сценариев можно сохранять не только текущий шаг, но и другие параметры, например, отмеченные опции:

intro.onchange(function() {
    const progress = {
        currentStep: intro._currentStep,
        completedSteps: intro._introItems
            .filter(item => item._introCompleted)
            .map(item => item.element.id)
    };
    localStorage.setItem('introProgress', JSON.stringify(progress));
});

const savedProgress = JSON.parse(localStorage.getItem('introProgress') || '{}');
if (savedProgress.currentStep !== undefined) {
    intro.start().goToStep(savedProgress.currentStep + 1).start();
}
  • JSON.stringify / JSON.parse позволяют хранить сложные структуры данных в виде строки.
  • Можно отслеживать завершённые шаги и другие состояния интерфейса, восстанавливая их при повторной загрузке страницы.

Рекомендации по использованию

  • Сохранять прогресс только после явного перехода на шаг (onchange) для минимизации записи в localStorage.
  • Проверять наличие сохранённых данных перед запуском руководства.
  • Всегда очищать данные при полном завершении, чтобы не возникали ошибки при изменении структуры шагов в будущем.
  • Использовать отдельные ключи для разных руководств, если на сайте их несколько.

Пример комплексного подхода

document.addEventListener('DOMContentLoaded', () => {
    const intro = introJs();
    intro.setOptions({
        steps: [
            { element: '#step1', intro: 'Первый шаг.' },
            { element: '#step2', intro: 'Второй шаг.' },
            { element: '#step3', intro: 'Третий шаг.' }
        ],
        showProgress: true,
        exitOnOverlayClick: false
    });

    const savedStep = localStorage.getItem('introjsCurrentStep');
    if (savedStep !== null) {
        intro.goToStep(Number(savedStep) + 1);
    }

    intro.onchange(function() {
        localStorage.setItem('introjsCurrentStep', intro._currentStep);
    });

    intro.oncomplete(function() {
        localStorage.removeItem('introjsCurrentStep');
    });

    intro.onexit(function() {
        localStorage.removeItem('introjsCurrentStep');
    });

    intro.start();
});

Такой подход обеспечивает плавное сохранение прогресса, возможность продолжения с последнего шага и корректное завершение руководства без утечек данных.