Разбиение на логические группы

Разбиение шагов на логические группы позволяет структурировать пользовательский тур, повышает читаемость сценария и упрощает сопровождение кода. В сложных интерфейсах последовательность шагов редко является линейной — чаще требуется делить её на смысловые блоки: регистрация, навигация, настройки, аналитика и т.д.

Принципы группировки

1. Семантическая целостность Каждая группа должна описывать один законченный сценарий или функцию интерфейса. Например:

  • Основные элементы страницы
  • Панель управления
  • Работа с формами

2. Независимость блоков Группы должны быть по возможности независимыми, чтобы их можно было запускать отдельно.

3. Минимальная связанность Шаги внутри группы логически связаны, но не зависят от шагов других групп.


Способы реализации групп

1. Разделение массивов шагов

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

const mainSteps = [
  {
    element: '#header',
    intro: 'Это шапка сайта'
  },
  {
    element: '#menu',
    intro: 'Основное меню навигации'
  }
];

const formSteps = [
  {
    element: '#input-name',
    intro: 'Введите имя'
  },
  {
    element: '#submit-btn',
    intro: 'Нажмите для отправки'
  }
];

Запуск конкретной группы:

introJs().setOptions({
  steps: mainSteps
}).start();

2. Использование функций-обёрток

Для удобства управления группами шагов используется функция, которая принимает нужную группу.

function startTour(steps) {
  introJs().setOptions({
    steps: steps
  }).start();
}

// Запуск
startTour(formSteps);

Это повышает переиспользуемость и делает код чище.


3. Хранение групп в единой структуре

Для крупных проектов удобнее хранить все группы в одном объекте.

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
];

Группировка через data-атрибуты

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 управляемыми даже в больших приложениях и облегчает дальнейшее развитие интерфейса.