Issues and Explanation
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. <sup>For more info, see my section at the bottom on the Infinite Page Refreshing Issue</sup>
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.
Multipage Tour Proof of Concept
Editable Plunk |
Runnable Demo
script.js
<!-- language: lang-js -->
$(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();
});
});
index.html:
<!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>
newPage.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:
Infinite Page Refreshing Issue
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 <kbd>Next</kbd> Button, the tour will call <code>showStep(<i>i</i>)</code> 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:
<sup>I've omitted regex based paths although Bootstrap Tour also supports them</sup>
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:
- it will launch the current step based off the value in local storage.
- when the current step loads, it will confirm that the path for the step is the same as the current url
- if not, it will redirect to the step path and start back over with step 1
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?"
-No!
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:
Use Absolute URLS Absolutely
Tip: Get a better sense of what's going on by passing in debug:true
when creating your tour, which will log every redirect: