Кнопка Cancel

Кнопка Cancel в библиотеке Shepherd.js предназначена для немедленного прерывания текущего тура. В отличие от навигационных кнопок (Next, Back), она завершает сценарий полностью, сбрасывая текущее состояние и скрывая все активные шаги.

Основные задачи кнопки:

  • Прекращение взаимодействия пользователя с туром
  • Очистка UI-элементов, добавленных Shepherd
  • Вызов соответствующих событий завершения

Поведение по умолчанию

При нажатии кнопки Cancel происходит:

  1. Остановка текущего шага
  2. Закрытие всплывающего элемента (tooltip)
  3. Удаление overlay (если используется)
  4. Генерация события cancel

Метод, который вызывается внутренне:

tour.cancel();

Это означает, что добавление кнопки Cancel по сути является привязкой к этому методу.


Добавление кнопки Cancel

Кнопка задаётся в массиве buttons внутри конфигурации шага:

const tour = new Shepherd.Tour();

tour.addStep({
  id: 'example-step',
  text: 'Описание шага',
  buttons: [
    {
      text: 'Cancel',
      action: () => tour.cancel()
    }
  ]
});

Ключевые свойства кнопки:

  • text — отображаемый текст
  • action — функция, выполняемая при нажатии

Использование встроенного контекста

Shepherd передаёт в action контекст текущего шага (this), что позволяет не ссылаться напрямую на tour:

buttons: [
  {
    text: 'Cancel',
    action: function () {
      this.cancel();
    }
  }
]

Такой подход особенно полезен при создании переиспользуемых конфигураций шагов.


Стилизация кнопки Cancel

Кнопке можно задать CSS-классы через свойство classes:

buttons: [
  {
    text: 'Cancel',
    action: () => tour.cancel(),
    classes: 'shepherd-button-secondary'
  }
]

Часто используется:

  • менее заметный стиль (secondary)
  • размещение слева от основной кнопки
  • уменьшенная визуальная значимость по сравнению с Next

Позиционирование среди других кнопок

Порядок кнопок определяется порядком в массиве:

buttons: [
  {
    text: 'Cancel',
    action: () => tour.cancel()
  },
  {
    text: 'Next',
    action: () => tour.next()
  }
]

Практика:

  • Cancel размещается слева
  • Next — справа (основное действие)

Обработка события cancel

Событие cancel позволяет выполнять дополнительную логику при отмене тура:

tour.on('cancel', () => {
  console.log('Тур был отменён');
});

Возможные сценарии:

  • сохранение состояния (например, пользователь отказался)
  • аналитика (отслеживание отказов)
  • очистка пользовательских данных

Отличие cancel от complete

Важно различать два сценария завершения тура:

Действие Метод Событие Значение
Отмена cancel() cancel Пользователь прервал
Завершение complete() complete Тур пройден полностью

Это различие критично для логики приложения.


Условное отображение кнопки

Кнопка Cancel может добавляться не на все шаги:

buttons: [
  {
    text: 'Cancel',
    action: () => tour.cancel(),
    classes: 'shepherd-button-secondary',
    disabled: false
  }
]

Или динамически:

if (shouldAllowCancel) {
  stepConfig.buttons.push({
    text: 'Cancel',
    action: () => tour.cancel()
  });
}

Кастомная логика отмены

Вместо прямого вызова tour.cancel() можно внедрить промежуточную логику:

buttons: [
  {
    text: 'Cancel',
    action: () => {
      if (confirm('Прервать тур?')) {
        tour.cancel();
      }
    }
  }
]

Другие примеры:

  • показ модального окна
  • сохранение прогресса
  • логирование действий пользователя

Отмена из внешнего кода

Кнопка Cancel — не единственный способ завершения тура. Метод cancel() можно вызвать из любого места:

document.querySelector('#exit-tour').addEventListener('click', () => {
  tour.cancel();
});

Это позволяет:

  • синхронизировать UI
  • добавлять глобальные элементы управления

Взаимодействие с overlay

Если включён затемняющий слой (useModalOverlay: true), кнопка Cancel:

  • убирает overlay
  • возвращает доступ ко всем элементам страницы

Дополнительно можно разрешить отмену кликом вне шага:

const tour = new Shepherd.Tour({
  useModalOverlay: true,
  exitOnEsc: true
});

Отмена через клавиатуру

По умолчанию поддерживается отмена через клавишу Escape:

exitOnEsc: true

Это эквивалентно нажатию кнопки Cancel.


Поведение при асинхронных шагах

Если шаг содержит асинхронную логику (например, загрузку данных), вызов cancel():

  • не прерывает асинхронные операции автоматически
  • требует ручного контроля

Пример:

let isCancelled = false;

tour.on('cancel', () => {
  isCancelled = true;
});

async function loadData() {
  const data = await fetch('/api/data');
  if (isCancelled) return;
  // обработка данных
}

Повторный запуск после отмены

После вызова cancel() тур можно запустить снова:

tour.start();

Но состояние шагов сбрасывается:

  • текущий индекс обнуляется
  • все шаги начинаются заново

Расширенные сценарии

Частичная отмена (с возвратом позже)

Можно сохранить текущий шаг:

let currentStepId;

tour.on('show', (event) => {
  currentStepId = event.step.id;
});

tour.on('cancel', () => {
  localStorage.setItem('tourStep', currentStepId);
});

Позже:

const savedStep = localStorage.getItem('tourStep');
if (savedStep) {
  tour.show(savedStep);
}

UX-рекомендации

  • Кнопка Cancel не должна доминировать визуально

  • Использование понятных текстов:

    • “Отмена”
    • “Пропустить”
    • “Закрыть”
  • Наличие подтверждения при критичных сценариях

  • Возможность повторного запуска тура


Частые ошибки

1. Отсутствие действия

{
  text: 'Cancel'
}

Кнопка не работает без action.

2. Неправильный контекст

action: function () {
  tour.cancel(); // может быть недоступен
}

Решение — использовать this.cancel().

3. Смешивание complete и cancel

Использование complete() вместо cancel() и наоборот приводит к логическим ошибкам в аналитике и пользовательском опыте.


Итоговая структура кнопки Cancel

{
  text: 'Cancel',
  action: function () {
    this.cancel();
  },
  classes: 'shepherd-button-secondary'
}

Эта конфигурация является базовой и может быть расширена в зависимости от требований интерфейса и бизнес-логики.