I'm having trouble initiating Bootstrap Tour on a multipage tour once I get to the second page.
I have the tour start with a click event and localStorage
is set to false
. The tour starts fine with the click event but then, when I go to the second step of the tour and load a new page, the tour does not pick up where it left off.
How do I go about resuming the tour from Step two on this new page? I know I need to re-initialize the tour, but I am apparently not doing this correctly.
$(document).ready(function () {
// Instance the tour
var tour = new Tour({
name: "CiteTour",
steps: [{
element: "",
title: "#1",
content: "You can find help with formatting citations on the Guide page. Click 'Next' to go there now.",
placement: ""
}, {
element: "#CiteTour2",
title: "#2 - Citation Resources",
content: "There are several options for getting help with formatting citations. Once on the Guide page, look for the box labeled 'Citation Help.'",
placement: "right",
path: "/newpath",
onNext: function (tour) {
tour.init();
tour.restart();
}
}, {
element: "#CiteTour3",
title: "#3",
content: "This site can help format your research paper and references in APA, MLA, and the other major citation formats.",
placement: "right",
}, {
element: "#AskTour1",
title: "#4 - Ask-a-Librarian",
content: "If you still have questions about citations or citation format, feel free to contact the librarians. Good luck!",
placement: "left",
}],
storage: false,
});
// Initialize the tour
tour.init();
$('#CiteTour-go').on('click', function () {
// Start the tour
tour.start();
});
});
First, you must make sure you are using storage: window.localStorage
, which uses the Storage API. This is the default tour option, so all you have to do is not override it to false as you have done. What this does is allow Bootstrap Tour to persist the current step information across multiple pages within the same domain.
Want Proof? - Open up your dev tools and see:
Second, if you are specifying the path
option for any step, you should specify it for all steps. When a single page tour starts, it doesn't need to worry about navigating to different pages, but as soon as you've moved to a new page, if you haven't specified paths for previous steps, bootstrap tour has no way of knowing where to navigate back to.
Furthermore, you need to use an absolute-path reference by prefxing the url with a single slash so it is relative to the root directory. If you use relative paths, the path will be changed as you move through pages/steps. For more info, see my section at the bottom on the Infinite Page Refreshing Issue
Third, as long as you define the tour
object and init
ialize it, the tour will pickup automatically on a new page.
Let's look at a simplified version of what init()
does:
Tour.prototype.init = function(force) {
// other code omitted for brevity
if (this._current !== null) {
this.showStep(this._current);
}
};
So once you've initialized the tour, as long as it notices that a tour has started and has not yet ended (i.e. it has a current step), it will automatically start-up that step. So you don't need to initialize by tapping into the onNext
event on your second step.
Editable Plunk | Runnable Demo
$(function() {
// define tour
var tour = new Tour({
steps: [{
path: "/index.html",
element: "#my-element",
title: "Title of my step",
content: "Content of my step"
}, {
path: "/newPage.html",
element: "#my-other-element",
title: "Title of my step",
content: "Content of my step"
}]
});
// init tour
tour.init();
// start tour
$('#start-tour').click(function() {
tour.restart();
});
});
<!DOCTYPE html>
<html>
<head>
<title>Multipage Bootstrap Tour - Page 1</title>
<link rel="stylesheet" href="bootstrap.css" />
<link rel="stylesheet" href="bootstrap-tour.min.css">
</head>
<body>
<div class="container">
<h1>First Page</h1>
<button class="btn btn-lg btn-primary" id="start-tour">
Start Tour
</button><br/><br/>
<span id="my-element">
My First Element
</span>
</div>
<script src="jquery.min.js"></script>
<script src="bootstrap.js"></script>
<script src="bootstrap-tour.min.js"></script>
<script src="script.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Multipage Bootstrap Tour - Page 2</title>
<link rel="stylesheet" href="bootstrap.css" />
<link rel="stylesheet" href="bootstrap-tour.min.css">
</head>
<body>
<div class="container">
<h1>New Page</h1>
<span id="my-other-element">
My Second Elemennt
</span>
</div>
<script src="jquery.min.js"></script>
<script src="bootstrap.js"></script>
<script src="bootstrap-tour.min.js"></script>
<script src="script.js"></script>
</body>
</html>
Where you've brought in the following libraries:
In a lot of configurations, you'll get into a loop where the page infinitely refreshes, continually attempting to resolve to the path of the current step. Here's a look into why this issue occurs and how to fix it.
How does Bootstrap Tour go to the next step?
When you hit the Next Button, the tour will call showStep(i)
for the next step
Here's a simplified version of showStep
:
Tour.prototype.showStep = function (i) {
// other code omitted for brevity
// get step path
path = tour._options.basePath + step.path;
// get current path - join location and hash
current_path = [document.location.pathname, document.location.hash].join('');
// determine if we need to redirect and do so
if (_this._isRedirect(path, current_path)) {
_this._redirect(step, path);
return;
}
};
So, if the current path in the document is different than the path for the next step, then tour will automatically redirect to the next step.
Here's a simplified form of the redirection that just takes into account string values:
I've omitted regex based paths although Bootstrap Tour also supports them
Tour.prototype._isRedirect = function(path, currentPath) {
var checkPath = path.replace(/\?.*$/, '').replace(/\/?$/, '');
var checkCurrent = currentPath.replace(/\/?$/, '');
return (checkPath !== checkCurrent);
};
Tour.prototype._redirect = function(step, path) {
this._debug("Redirect to " + path);
return document.location.href = path;
};
Note: The regex is just there to remove query parameters (
/\?.*$/
) and trailing forward slashes (//?$/`)
When any page loads, it's not sure if Bootstrap Tour has redirected it, or you're just coming back and trying to pickup the tour where you left off.
So on any page when you initialize the tour:
In other words, it knows how to get to where it needs to go next, but has no way of confirming if that's the case once it gets there. Take this situation for example with a step that looks like this:
var step = {
path: "index.html",
element: "#my-element",
title: "Title of my step",
content: "Content of my step"
}
It can be redirected to the relative reference just fine, but when the page loads again, and checks that it has been loaded at the correct address, this will happen:
"KyleMit", you might protest, "can't it just figure out what I want?"
If you rely on relative paths for redirection, when it's loading a step, it can't gaurantee that you've actually arrived at the step and it will try to redirect you again.
That's because, in web addresses, "index.html" !== "\index.html"
. They are two different paths! One is guaranteed to be at the domain root, while the other could be anywhere. Imagine you have some nested views like this:
When navigating between pages, how can bootstrap know if it's arrived at the correct destination if you've only told it the correct page name.
Which brings us to the resolution of this issue:
Tip: Get a better sense of what's going on by passing in
debug:true
when creating your tour, which will log every redirect: