sorich87 / bootstrap-tour

Quick and easy product tours with Twitter Bootstrap Popovers
http://bootstraptour.com
MIT License
4.44k stars 942 forks source link

Conditionally skip step #651

Open vanderlee opened 7 years ago

vanderlee commented 7 years ago

Is there a way to supply a callback to a step to determine whether the step should be displayed or not? For instance if the webpage is in a specific state that is more complex than can be detected using element and orphan skipping?

I expected the onShow() handler to skip a step if returning a falsey value, but the return seems to be ignored.

p.s. onShow() documentation lists only the first argument. The second argument (0-based index of step) is not documented).

jcgiuffrida commented 7 years ago

You have to manually tell it to advance to the next step. onShow() and onShown() are functions to run, but don't govern whether or not the step is shown.

I use the onShown() handler with a custom callback to skip a step. For instance:

// In tour step
{
  ...
  onShown: nextStepIfTrue,
  ...
}

/** Advances to the next step if a condition is true */
function nextStepIfTrue(tour){
  if (condition){
    tour.next();
  }

This might work with onShow() as well, but there could be an inconsistency between the step you think you're on, and the step the user's computer thinks it's on if it stores the tour state.

You can generalize this to run any arbitrary callback, and advance the tour if the callback returns true, as you appear to be looking for, as follows:

// In tour step
{
  ...
  onShown: nextStepIfTrue,
  callback: function(){
    return foo === bar;
  },
  ...
}

/** Advances to the next step if the "callback" function is true. */
function nextStepIfTrue(tour){
  var currentStep = tour._options.steps[tour._current];
  if (currentStep.callback && currentStep.callback()){
    tour.next();
  }
}
vanderlee commented 7 years ago

Thanks. I've elaborated a bit on your examples to generalize it a bit more and make it work for both next and prev directions by remembering the previous step number. This can probably be improved upon (not quite sure if tour._previous is set at the right time), but it seems to work:

function skipStepIfTrue(tour) {
  var currentStep = tour._options.steps[tour._current];
  if (currentStep.skip && currentStep.skip(tour._current)) {
    tour[tour._current < tour._previous? 'prev' : 'next']();
  }
  tour._previous = tour._current;               
}

// In tour options
{
  ...
  onShown: skipStepIfTrue,
  ...
}

// In tour step (optional)
{
  ...
  skip: function(step) {
    return foo === bar;
  }
  ...
}