Rich callback system for handling tour and hint lifecycle events, enabling custom behavior and integration with analytics or other systems.
Comprehensive event system for Tour instances with lifecycle hooks and interaction events.
/**
* Callback fired before changing to a new step
* Return false to prevent the step change
*/
type introBeforeChangeCallback = (
this: Tour,
targetElement: HTMLElement,
currentStep: number,
direction: "backward" | "forward"
) => Promise<boolean> | boolean;
/**
* Callback fired when changing to a new step
*/
type introChangeCallback = (
this: Tour,
targetElement: HTMLElement
) => void | Promise<void>;
/**
* Callback fired after changing to a new step
*/
type introAfterChangeCallback = (
this: Tour,
targetElement: HTMLElement
) => void | Promise<void>;
/**
* Callback fired when tour is completed
*/
type introCompleteCallback = (
this: Tour,
currentStep: number,
reason: "skip" | "end" | "done"
) => void | Promise<void>;
/**
* Callback fired when tour is started
*/
type introStartCallback = (
this: Tour,
targetElement: HTMLElement
) => void | Promise<void>;
/**
* Callback fired when tour is exited
*/
type introExitCallback = (
this: Tour
) => void | Promise<void>;
/**
* Callback fired when tour is skipped
*/
type introSkipCallback = (
this: Tour,
currentStep: number
) => void | Promise<void>;
/**
* Callback fired before tour exit
* Return false to prevent the exit
*/
type introBeforeExitCallback = (
this: Tour,
targetElement: HTMLElement
) => boolean | Promise<boolean>;Methods for registering event callbacks on Tour instances.
/**
* Set callback for before step change event
* @param callback - Function to call before changing steps
*/
onBeforeChange(callback: introBeforeChangeCallback): Tour;
/**
* Set callback for step change event
* @param callback - Function to call when changing steps
*/
onChange(callback: introChangeCallback): Tour;
/**
* Set callback for after step change event
* @param callback - Function to call after changing steps
*/
onAfterChange(callback: introAfterChangeCallback): Tour;
/**
* Set callback for tour completion event
* @param callback - Function to call when tour completes
*/
onComplete(callback: introCompleteCallback): Tour;
/**
* Set callback for tour start event
* @param callback - Function to call when tour starts
*/
onStart(callback: introStartCallback): Tour;
/**
* Set callback for tour exit event
* @param callback - Function to call when tour exits
*/
onExit(callback: introExitCallback): Tour;
/**
* Set callback for tour skip event
* @param callback - Function to call when tour is skipped
*/
onSkip(callback: introSkipCallback): Tour;
/**
* Set callback for before tour exit event
* @param callback - Function to call before tour exits
*/
onBeforeExit(callback: introBeforeExitCallback): Tour;
/**
* Get a specific callback function
* @param callbackName - Name of the callback to retrieve
*/
callback<K extends keyof TourCallbacks>(callbackName: K): TourCallbacks[K] | undefined;
interface TourCallbacks {
onBeforeChange?: introBeforeChangeCallback;
onChange?: introChangeCallback;
onAfterChange?: introAfterChangeCallback;
onComplete?: introCompleteCallback;
onStart?: introStartCallback;
onExit?: introExitCallback;
onSkip?: introSkipCallback;
onBeforeExit?: introBeforeExitCallback;
}Basic Event Handling:
import introJs from "intro.js";
const tour = introJs.tour()
.addSteps([
{ title: "Welcome", intro: "Welcome to our app!", element: "#welcome" },
{ title: "Features", intro: "Check out these features", element: "#features" },
{ title: "Settings", intro: "Configure your preferences", element: "#settings" }
]);
// Track tour start
tour.onStart(function(targetElement) {
console.log("Tour started on:", targetElement);
// Analytics tracking
gtag('event', 'tour_started', {
'event_category': 'onboarding',
'event_label': 'product_tour'
});
});
// Track step changes
tour.onChange(function(targetElement) {
const currentStep = this.getCurrentStep();
console.log(`Moved to step ${currentStep + 1}`);
// Track step engagement
gtag('event', 'tour_step_viewed', {
'event_category': 'onboarding',
'step_number': currentStep + 1
});
});
// Handle tour completion
tour.onComplete(function(currentStep, reason) {
console.log(`Tour completed. Reason: ${reason}`);
// Track completion
gtag('event', 'tour_completed', {
'event_category': 'onboarding',
'completion_reason': reason,
'steps_completed': currentStep + 1
});
// Show success message
if (reason === "done") {
showSuccessMessage("Great! You've completed the tour.");
}
});
await tour.start();Advanced Event Handling with Conditional Logic:
import introJs from "intro.js";
const tour = introJs.tour();
// Conditional step progression
tour.onBeforeChange(function(targetElement, currentStep, direction) {
console.log(`About to ${direction} from step ${currentStep}`);
// Validate user action before proceeding
if (currentStep === 1 && direction === "forward") {
const isFormValid = validateRequiredForm();
if (!isFormValid) {
alert("Please complete the required form before continuing.");
return false; // Prevent step change
}
}
// Allow step change
return true;
});
// Custom step-specific behavior
tour.onAfterChange(function(targetElement) {
const currentStep = this.getCurrentStep();
const stepData = this.getStep(currentStep);
// Execute step-specific actions
switch (currentStep) {
case 0:
// First step: highlight important UI elements
highlightElement("#main-navigation");
break;
case 1:
// Second step: load dynamic content
loadUserDashboardData();
break;
case 2:
// Third step: enable interactive features
enableDemoMode();
break;
}
});
// Prevent exit unless confirmed
tour.onBeforeExit(function(targetElement) {
if (this.getCurrentStep() !== undefined && this.getCurrentStep() < this.getSteps().length - 1) {
return confirm("Are you sure you want to exit the tour?");
}
return true;
});Event system for Hint instances with lifecycle and interaction events.
/**
* Callback fired when hints are added to the page
*/
type hintsAddedCallback = (
this: Hint
) => void | Promise<void>;
/**
* Callback fired when a hint is clicked
*/
type hintClickCallback = (
this: Hint,
item: HintItem
) => void | Promise<void>;
/**
* Callback fired when a hint is closed
*/
type hintCloseCallback = (
this: Hint,
item: HintItem
) => void | Promise<void>;Methods for registering event callbacks on Hint instances.
/**
* Set callback for when hints are added to the page
* @param callback - Function to call when hints are rendered
*/
onHintsAdded(callback: hintsAddedCallback): Hint;
/**
* Set callback for hint click events
* @param callback - Function to call when hint is clicked
*/
onHintClick(callback: hintClickCallback): Hint;
/**
* Set callback for hint close events
* @param callback - Function to call when hint is closed
*/
onHintClose(callback: hintCloseCallback): Hint;
/**
* Get a specific callback function
* @param callbackName - Name of the callback to retrieve
*/
callback<K extends keyof HintCallbacks>(callbackName: K): HintCallbacks[K] | undefined;
interface HintCallbacks {
onHintsAdded?: hintsAddedCallback;
onHintClick?: hintClickCallback;
onHintClose?: hintCloseCallback;
}Basic Hint Event Handling:
import introJs from "intro.js";
const hint = introJs.hint()
.addHint({
element: "#help-button",
hint: "Click for help and support",
hintPosition: "top-middle"
})
.addHint({
element: "#settings-gear",
hint: "Access your account settings",
hintPosition: "bottom-left"
});
// Track when hints are rendered
hint.onHintsAdded(function() {
console.log("All hints have been added to the page");
// Analytics tracking
gtag('event', 'hints_displayed', {
'event_category': 'help_system',
'hint_count': this.getHints().length
});
});
// Track hint interactions
hint.onHintClick(function(item) {
console.log("Hint clicked:", item);
// Track which hints are most useful
gtag('event', 'hint_clicked', {
'event_category': 'help_system',
'hint_element': item.element?.id || 'unknown',
'hint_content': item.hint
});
// Show detailed help if needed
if (item.hint?.includes("settings")) {
showSettingsHelp();
}
});
// Track hint dismissals
hint.onHintClose(function(item) {
console.log("Hint closed:", item);
gtag('event', 'hint_closed', {
'event_category': 'help_system',
'hint_element': item.element?.id || 'unknown'
});
});
await hint.render();Advanced Hint Event Handling:
import introJs from "intro.js";
const hint = introJs.hint();
// Dynamic hint content based on user context
hint.onHintClick(async function(item) {
const element = item.element;
if (!element) return;
// Get contextual information
const userRole = getUserRole();
const featureAccess = await checkFeatureAccess(element.id);
// Customize hint behavior based on context
if (!featureAccess) {
showUpgradePrompt();
return;
}
// Show role-specific guidance
const roleSpecificHint = getHintForRole(item.hint, userRole);
if (roleSpecificHint !== item.hint) {
// Update hint content dynamically
item.hint = roleSpecificHint;
this.refresh(); // Refresh to show updated content
}
// Track contextual usage
gtag('event', 'contextual_hint_used', {
'user_role': userRole,
'feature_id': element.id,
'has_access': featureAccess
});
});
// Auto-close hints after user interaction
hint.onHintClick(function(item) {
// Close hint after 3 seconds
setTimeout(() => {
this.hideHintDialog();
}, 3000);
});Analytics Integration:
import introJs from "intro.js";
// Comprehensive analytics tracking
function setupTourAnalytics(tour) {
const startTime = Date.now();
let stepTimes = [];
tour.onStart(function() {
gtag('event', 'tour_started', {
'event_category': 'onboarding',
'timestamp': startTime
});
});
tour.onChange(function() {
stepTimes.push(Date.now());
});
tour.onComplete(function(currentStep, reason) {
const totalTime = Date.now() - startTime;
const avgStepTime = stepTimes.length > 0 ?
stepTimes.reduce((a, b, i) => a + (i > 0 ? b - stepTimes[i-1] : 0), 0) / stepTimes.length : 0;
gtag('event', 'tour_completed', {
'event_category': 'onboarding',
'completion_reason': reason,
'total_time_ms': totalTime,
'avg_step_time_ms': avgStepTime,
'steps_completed': currentStep + 1
});
});
return tour;
}
// Usage
const tour = setupTourAnalytics(introJs.tour());State Management Integration:
import introJs from "intro.js";
// Redux/state management integration
function connectTourToStore(tour, store) {
tour.onStart(function() {
store.dispatch({ type: 'TOUR_STARTED' });
});
tour.onChange(function() {
const currentStep = this.getCurrentStep();
store.dispatch({
type: 'TOUR_STEP_CHANGED',
payload: { step: currentStep }
});
});
tour.onComplete(function(currentStep, reason) {
store.dispatch({
type: 'TOUR_COMPLETED',
payload: { step: currentStep, reason }
});
});
return tour;
}Legacy callback methods maintained for backward compatibility:
/**
* @deprecated Use onBeforeChange instead
*/
onbeforechange(callback: introBeforeChangeCallback): Tour;
/**
* @deprecated Use onChange instead
*/
onchange(callback: introChangeCallback): Tour;
/**
* @deprecated Use onAfterChange instead
*/
onafterchange(callback: introAfterChangeCallback): Tour;
/**
* @deprecated Use onComplete instead
*/
oncomplete(callback: introCompleteCallback): Tour;
/**
* @deprecated Use onStart instead
*/
onstart(callback: introStartCallback): Tour;
/**
* @deprecated Use onExit instead
*/
onexit(callback: introExitCallback): Tour;
/**
* @deprecated Use onSkip instead
*/
onskip(callback: introSkipCallback): Tour;
/**
* @deprecated Use onBeforeExit instead
*/
onbeforeexit(callback: introBeforeExitCallback): Tour;
/**
* @deprecated Use onHintsAdded instead
*/
onhintsadded(callback: hintsAddedCallback): Hint;
/**
* @deprecated Use onHintClick instead
*/
onhintclick(callback: hintClickCallback): Hint;
/**
* @deprecated Use onHintClose instead
*/
onhintclose(callback: hintCloseCallback): Hint;