I would like to Trigger some JS only the first time a user logs in, and only the first time a specific page is loaded.
I believe I can deal with the first time they log in, by simply checking user.sign_in_count < 2
, but I don't know how to specify just on the first page load only.
i.e. I don't want the JS to be triggered after the user logs in for the first time and refreshes the page without logging out.
I am using Turbolinks and $(document).on('turbolinks:load', function() {
to trigger it.
Edit 1
So what I am trying to do is execute Bootstrap Tour on a number of pages. But I only want that tour to be automatically executed, on the first page load. The tour itself will lead the user to other specific pages within my app, but each of those pages will have page-specific tour JS on each page.
Right now, in my HTML I have something like this:
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
// Initialize the tour
tour.init();
// Start the tour
tour.start();
});
</script>
So all I really want to do is the following:
The real problem is just within that first sign in, if they refresh the main page 10 times, this tour gets executed 10 times. That's what I am trying to stop.
I hope that provides some more clarity.
Preface
It's my understanding that you have:
script
value based upon first signinSolution Overview
The solution below uses localStorage
to store a key value pair of each tour's identifier and if it has been seen or not. localStorage
persists between page refreshes and sessions, as the name suggests, localStorage
is unique to each domain, device, and browser (ie. chrome's localStorage
cannot access firefox's localStorage
even for the same domain, nor can chrome's localStorage
on your laptop access chrome's localStorage
on your mobile even for the same domain). I raise this to illustrate the reliance upon Preface 3 to toggle a JS flag for if the user has logged in previously.
For the tour to start, the code checks localStorage
for if its corresponding key value pair is not set to true
(representing having been "seen"). If it does exist and is set to true, the tour does not start, otherwise it runs. When each tour begins, using its onStart
method, we update/add the tour's identifier to localStorage
and set its value to true
.
Manual execution of the tour can be performed by either manually calling the tour's start
method if you would like only the current page's tour to execute, otherwise, you can clear out all of the localStorage
related to the tour and send the user back to the first page/if you're on the first page, again just call the start
method.
JSFiddle (HTML based off other question's you've asked regarding touring)
HTML (this could be any element with the id="tourAgain"
attribute for the following code to work.
<button class="btn btn-sm btn-default" id="tourAgain">Take Tour Again</button>
JS
var isFirstLogin = true; // this value is populated by ruby based upon first login
var userID = 12345; // this value is populated by ruby based upon current_user.id, change this value to reset localStorage if isFirstLogin is true
// jquery on ready function
$(function() {
var $els = {}; // storage for our jQuery elements
var tour; // variable that will become our tour
var tourLocalStorage = JSON.parse(localStorage.getItem('myTour')) || {};
function activate(){
populateEls();
setupTour();
$els.tourAgain.on('click', tourAgain);
// only check check if we should start the tour if this is the first time we've logged in
if(isFirstLogin){
// if we have a stored userID and its different from the one passed to us from ruby
if(typeof tourLocalStorage.userID !== "undefined" && tourLocalStorage.userID !== userID){
// reset the localStorage
localStorage.removeItem('myTour');
tourLocalStorage = {};
}else if(typeof tourLocalStorage.userID === "undefined"){ // if we dont have a userID set, set it and save it to localStorage
tourLocalStorage.userID = userID;
localStorage.setItem('myTour', JSON.stringify(tourLocalStorage));
}
checkShouldStartTour();
}
}
// helper function that creates a cache of our jQuery elements for faster lookup and less DOM traversal
function populateEls(){
$els.body = $('body');
$els.document = $(document);
$els.tourAgain = $('#tourAgain');
}
// creates and initialises a new tour
function setupTour(){
tour = new Tour({
name: 'homepage', // unique identifier for each tour (used as key in localStorage)
storage: false,
backdrop: true,
onStart: function() {
tourHasBeenSeen(this.name);
$els.body.addClass('is-touring');
},
onEnd: function() {
console.log('ending tour');
$els.body.removeClass('is-touring');
},
steps: [{
element: "div.navbar-header img.navbar-brand",
title: "Go Home",
content: "Go home to the main page."
}, {
element: "div.navbar-header input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}, {
element: "span.num-players",
title: "Number of Players",
content: "This is the number of players that are in our database for this Tournament"
}, {
element: '#page-wrapper div.contact-box.profile-24',
title: "Player Info",
content: "Here we have a quick snapshot of the player stats"
}]
});
// Initialize the tour
tour.init();
}
// function that checks if the current tour has already been taken, and starts it if not
function checkShouldStartTour(){
var tourName = tour._options.name;
if(typeof tourLocalStorage[tourName] !== "undefined" && tourLocalStorage[tourName] === true){
// if we have detected that the tour has already been taken, short circuit
console.log('tour detected as having started previously');
return;
}else{
console.log('tour starting');
tour.start();
}
}
// updates localStorage with the current tour's name to have a true value
function tourHasBeenSeen(key){
tourLocalStorage[key] = true;
localStorage.setItem('myTour', JSON.stringify(tourLocalStorage));
}
function tourAgain(){
// if you want to tour multiple pages again, clear our localStorage
localStorage.removeItem('myTour');
// and if this is the first part of the tour, just continue below otherwise, send the user to the first page instead of using the function below
// if you just want to tour this page again just do the following line
tour.start();
}
activate();
});
PS. the reason we dont use onEnd
to trigger the tourHasBeenSeen
function is that there is currently a bug with bootstrap tour where if the last step's element doesnt exist, the tour ends without triggering the onEnd
callback, BUG.
You could try using Javascript's sessionStorage
, which is deleted when the user closes the tab, but survives through refreshes. Just use sessionStorage.setItem(key, value
and sessionStorage.getItem(key)
. Remember that sessionStorage
can only store strings!
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
if(sessionStorage.getItem("loggedIn") !== "yes"){//Remember that sessionStorage can only store strings!
//Initialize the tour
tour.init();
// Start the tour
tour.start();
}
else{
//Set item "loggedIn" in sessionStorage to "yes"
sessionStorage.putItem("loggedIn", "yes");
}
var goBackToTour = function(e){
//You can also make a "fake" link, so that it looks like a link, but is not, and you don't have to put the following line:
e.preventDefault();
tour.init();
tour.start();
};
document.getElementById("goBackToTourLink").addEventListener("click", goBackToTour);
});
//On the logout
var logout = function(){
sessionStorage.setItem("loggedIn", "no");
};
</script>
You can store if user has seen the tour or not in the cookie. You can maintain a "TrackingCookie" which has all the user tracking information (eg. tour_shown, promotion_shown etc, which is accessed by your javascript code. Following TrackingCookie code is to maintain all such tracking information in one cookie. I am calling it tracking_cookie.
Cookies can be accessed server-side using
cookies[:tracking_cookie]
var TrackingCookie = (function() {
function TrackingCookie() {
this.name = 'tracking_cookie';
this.expires = new Date(new Date().setYear(new Date().getFullYear() + 1));
}
TrackingCookie.prototype.set = function(name, value) {
var data={};
if(!this.readFromStore()) {
data = this.readFromStore();
}
data[name] = value;
return this.writeToStore(data);
};
TrackingCookie.prototype.set_if_unset = function(name, value) {
if (!this.get(name)) {
return this.set(name, value);
}
};
TrackingCookie.prototype.get = function(name) {
return this.readFromStore()[name];
};
TrackingCookie.prototype.writeToStore = function(data) {
return $.cookie(this.name, JSON.stringify(data), {
path: '/',
expires: this.expires
});
};
TrackingCookie.prototype.readFromStore = function() {
return $.parseJSON($.cookie(this.name));
};
return TrackingCookie;
})();
<script type="text/javascript">
$(document).on('turbolinks:load', function() {
//Instantiate the cookie
var tracking_cookie = new TrackingCookie();
//Cookie value not set means, it is a new user.
if(!tracking_cookie.get("tour_shown")){
//Set the value to be true.
tracking_cookie.set("tour_shown",true)
var tour = new Tour({
storage: false,
backdrop: true,
onStart: function(){
$('body').addClass('is-touring');
},
onEnd: function(){
$('body').removeClass('is-touring');
},
steps: [
{
element: "#navbar-logo",
title: "Go Home",
content: "All throughout the app, you can click our logo to get back to the main page."
},
{
element: "input#top-search",
title: "Search",
content: "Here you can search for players by their name, school, positions & bib color (that they wore in our tournament)"
}
]});
// Initialize the tour
tour.init();
// Start the tour
tour.start();
};
});
</script>
The cookie class is verbose. You can just use $.cookie
to achieve simple one toggle behavior. The above code works for all first time users, logged-in as well as logged-out. If you just want it for logged-in user, set the flag on user log-in on server-side.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With