Hopscotch tour step on dynamically generated content

I searched all over the place for a solution to this issue and this post is the one that came the closest but not quite definitive solution, so here it goes:

{ // This is the previous step
    element: "#idElement",
    title: "The Title",
    content: "The Content",
    onNext: function(tour) {
            tour.end();
            var checkExist = setInterval(function() {
                // This is the element from the next step. 
                $element = $('#idElementFromNextStep'); 

                if ($element.is(':visible')) {
                    clearInterval(checkExist);
                    tour.start(true); // True is to force the tour to start
                    tour.goTo(1); // The number is your next index (remember its base 0)
                }
            }, 100);
    },
    multipage: true, // Required
    orphan: true // Recommended
},{ // This is the step that was not working
    element: "#idElementFromNextStep",
    title: "Title of the step",
    content: "Details of the step",
    orphan: true,
}

So what this does is basically stop the tour when the next is fired, wait for the element to be added to the DOM and then restart the tour from the correct step by its index.

I borrowed a bit of code from @Luksurious. His solution kind of works (not for Bootstrap Tour though) but in any case it generates a flick when it loads the next step and goes back to the correct one.

I strongly advise against using a delay, it might seem to work on your local environment but its extremely dangerous to speculate with how long it takes a client to load.

Hope it helps someone else!


One hackaround I'm using is the delay field on each step. That is:

{
  title: "+Log",
  content: "Lipsum",
  target: "#log_activity_nav",
  placement: "left",
  nextOnTargetClick: true


},

{

  title: "Phone",
  content: "The bottom section is where each individual log and detail lives, sorted by importance.",
  target: "#activity_log_activity_log_brokers_attributes_0_contact_attributes_phone", // this target is part of an element that is dynamically generated
  delay: 2000, // wait 2 seconds for this element to load
  placement: "top"
},

This seems to fill my needs for now, although obviously it's not guaranteed to work depending on what you're loading.

If anyone has a more elegant solution I'm all ears!


What I do to wait for AJAX calls is the following:

{
  title: "Title",
  content: "Content",
  target: '.triggerAjax',
  placement: 'bottom',
  onNext: function() {
    $('.triggerAjax').click();

    var checkExist = setInterval(function() {
      $element = $('.dynamicallyLoaded');

      if ($element.is(':visible')) {
        clearInterval(checkExist);

        window.hopscotch.startTour(window.hopscotch.getCurrTour(), window.hopscotch.getCurrStepNum());
      }
    }, 100);
  },
  multipage: true
},

Using multipage tells hopscotch to not continue. Then the onNext function checks if the element we want to target next is present and starts the tour again.

Of course, if you need this more often, extract it as a general function to keep your code clean and short.