Разбиение шагов на логические группы позволяет структурировать пользовательский тур, повышает читаемость сценария и упрощает сопровождение кода. В сложных интерфейсах последовательность шагов редко является линейной — чаще требуется делить её на смысловые блоки: регистрация, навигация, настройки, аналитика и т.д.
1. Семантическая целостность Каждая группа должна описывать один законченный сценарий или функцию интерфейса. Например:
2. Независимость блоков Группы должны быть по возможности независимыми, чтобы их можно было запускать отдельно.
3. Минимальная связанность Шаги внутри группы логически связаны, но не зависят от шагов других групп.
Наиболее простой и очевидный способ — создание отдельных массивов шагов для каждой группы.
const mainSteps = [
{
element: '#header',
intro: 'Это шапка сайта'
},
{
element: '#menu',
intro: 'Основное меню навигации'
}
];
const formSteps = [
{
element: '#input-name',
intro: 'Введите имя'
},
{
element: '#submit-btn',
intro: 'Нажмите для отправки'
}
];
Запуск конкретной группы:
introJs().setOptions({
steps: mainSteps
}).start();
Для удобства управления группами шагов используется функция, которая принимает нужную группу.
function startTour(steps) {
introJs().setOptions({
steps: steps
}).start();
}
// Запуск
startTour(formSteps);
Это повышает переиспользуемость и делает код чище.
Для крупных проектов удобнее хранить все группы в одном объекте.
const tours = {
main: [
{ element: '#header', intro: 'Шапка' },
{ element: '#menu', intro: 'Меню' }
],
settings: [
{ element: '#settings-btn', intro: 'Настройки' },
{ element: '#theme-select', intro: 'Выбор темы' }
]
};
Запуск:
introJs().setOptions({
steps: tours.settings
}).start();
Иногда шаги зависят от состояния приложения. В таком случае группы формируются динамически.
function getTourSteps(userRole) {
const steps = [];
steps.push({
element: '#dashboard',
intro: 'Панель управления'
});
if (userRole === 'admin') {
steps.push({
element: '#admin-panel',
intro: 'Административная панель'
});
}
return steps;
}
Возможна комбинация нескольких групп в один сценарий.
const combinedSteps = [
...tours.main,
...tours.settings
];
introJs().setOptions({
steps: combinedSteps
}).start();
Такой подход полезен для построения расширенных туров.
Иногда требуется переход от одной группы к другой после завершения текущей.
const intro = introJs();
intro.setOptions({
steps: tours.main
});
intro.oncomplete(function() {
intro.setOptions({
steps: tours.settings
}).start();
});
intro.start();
Для предотвращения повторного показа используются флаги:
if (!localStorage.getItem('mainTourSeen')) {
introJs().setOptions({
steps: tours.main
}).start();
localStorage.setItem('mainTourSeen', true);
}
В многостраничных приложениях группы привязываются к конкретным страницам:
const pageTours = {
home: [...],
profile: [...],
dashboard: [...]
};
Определение текущей страницы:
const currentPage = window.location.pathname;
if (currentPage === '/dashboard') {
introJs().setOptions({
steps: pageTours.dashboard
}).start();
}
Группы можно вызывать по имени, как маршруты:
function runTourByName(name) {
if (tours[name]) {
introJs().setOptions({
steps: tours[name]
}).start();
}
}
runTourByName('main');
Группы могут отражать уровень подготовки пользователя:
const beginnerTour = [...];
const advancedTour = [...];
Выбор:
const userLevel = 'beginner';
introJs().setOptions({
steps: userLevel === 'beginner' ? beginnerTour : advancedTour
}).start();
Некоторые шаги могут использоваться в разных группах:
const commonSteps = [
{
element: '#help',
intro: 'Помощь'
}
];
const extendedTour = [
...tours.main,
...commonSteps
];
HTML позволяет логически разделять шаги прямо в разметке:
<div data-intro="Шаг 1" data-group="main"></div>
<div data-intro="Шаг 2" data-group="settings"></div>
Сбор шагов:
function getStepsByGroup(groupName) {
const elements = document.querySelectorAll(`[data-group="${groupName}"]`);
return Array.from(elements).map(el => ({
element: el,
intro: el.getAttribute('data-intro')
}));
}
Для оптимизации загрузки можно подгружать группы только при необходимости:
async function loadTour(name) {
const module = await import(`./tours/${name}.js`);
introJs().setOptions({
steps: module.default
}).start();
}
Хорошей практикой является хранение шагов отдельно от логики:
// steps.js
export const steps = {
main: [...],
profile: [...]
};
// main.js
import { steps } from './steps.js';
introJs().setOptions({
steps: steps.main
}).start();
В крупных проектах структура может выглядеть так:
/tours
/main
steps.js
/settings
steps.js
index.js
// index.js
import main from './main/steps';
import settings from './settings/steps';
export default {
main,
settings
};
Внутри группы можно явно задавать порядок:
const orderedSteps = [
{ element: '#one', intro: 'Первый', order: 2 },
{ element: '#two', intro: 'Второй', order: 1 }
].sort((a, b) => a.order - b.order);
Если элемент отсутствует, шаг можно пропустить:
const safeSteps = steps.filter(step => {
return document.querySelector(step.element);
});
Добавление служебной информации:
const tours = {
main: {
title: 'Основной тур',
steps: [...]
}
};
Использование:
introJs().setOptions({
steps: tours.main.steps
}).start();
Для сложной логики:
class TourManager {
constructor(tours) {
this.tours = tours;
}
start(name) {
if (!this.tours[name]) return;
introJs().setOptions({
steps: this.tours[name]
}).start();
}
}
const manager = new TourManager(tours);
manager.start('main');
Группы часто запускаются по событиям:
document.querySelector('#help-btn').addEventListener('click', () => {
introJs().setOptions({
steps: tours.main
}).start();
});
Группы могут объединяться в сценарии более высокого уровня:
const onboard ing = [
...tours.main,
...tours.settings,
...tours.profile
];
Можно отслеживать текущий шаг:
introJs().onchange(function(targetElement) {
console.log('Текущий элемент:', targetElement);
});
Эффективная группировка включает:
Такая организация делает туры в Intro.js управляемыми даже в больших приложениях и облегчает дальнейшее развитие интерфейса.