Stop / reset JavaScript in a storyline

Jan 14, 2021

Hello,  I have a timer that I built from a Storyline provided in another post; it uses a javascript snippet to start the timer and works great however, I am trying to add a feature to stop the timer and/or reset it (both would be nice but either would be helpful).  Can anyone provide some help?  

17 Replies
Math Notermans

Basically it is scope that makes most timers used onhere ( the one from Toni too ) tough to stop/pause and/or resume.

Scope means that if you define some javascript variable on some spot, it will not automatically be available in another spot. Eg. if you create a trigger on a slide with a function in it.... that function won't be available in any other slide. In fact it wont even be available in another trigger on that slide. It only is available in that particular trigger.

To overcome that you have 2 possibilities.
- Use Storyline variables to save and pass Javascript vars. SL vars are in a global scope.
- Add your Javascript functions and code to the HTML. By using external JS files like my generic_functions setup you can create global JS code that can be used at any time any where.
https://community.articulate.com/discussions/articulate-storyline/including-javascript-for-repeating-functionality )

Sam G

Hi Math

Thanks for your reply and link to article about scope and adding JS functions to package.

If I understand the issue with the timer correctly - you can only access the function that operates the timer in the same slide and same trigger that initialize it.

So - as you say to use SL variables which have global scope, can you not have a variable called "pause" which fires on any slide you want if triggered, and a reference to that variable in the JS function that started the timer? So that whenever the pause variable is set to true - the JS timer will pause (provided of course the function is correctly coded to stop counting when pause = true)

Ditto for reset 

Math Notermans

Hi Sam,

Yeah basically thats correct. The difficulty in working with Storyline variables and scope for a timer is that with only one variable for pause it won't work.  Say you initialize your timer on the first slide, lets call it slide A with trigger tA... the scope for that timer is then exactly there. You can tell the global variable ( varT = true ) the timer is running...and thus..when you get to slide B you do know the timer is running... and you can change the global variable ( varT = off )...but you then donot have access to the scope of the timer...because that is on slide A with trigger tA.

So when using variables only you need to add the timer itself to a global variable, so you access it anywhere. In fact that is cumbersome because in the end it takes quite a few variables for it to work. I prefer a global script on root level... i will make a sample when time permits this week.

Sam G

Hi Math

Sorry to keep asking questions!

Doesnt the JS function just kind of keep on running once started, and then use the latest value of SL global variable when the getplayer function is called in the counter function? So that if pause=true, set on any slide, on its next cycle to add 1 second to the counter time, the js function will use pause=true and thus pause? 

Math Notermans

The moment you leave a slide...all JS related things not set in global variables or  Storyline variables ( those are global too ) are gone. Im gonna make some samples in Storyline to share to explain the scope thing better. That way its a lot clearer.

The sample you sent me works because it uses Date as a global object. The current Date and Time are always the same ( timezone dependant ofcourse ) and each slide compares and calculates timedifference with the start time.

I will make samples showing explaining scope and a global timer script solution.
Only not this week ;-)

Christoph Krieger

I have updated a javascript countdown/timer example I have created several years ago.
You can start, pause or reset the timer.

https://360.articulate.com/review/content/45c48644-63e2-427c-bf96-ab8f1d06360c/review

You just need to adjust the SL variable: timeINmilliseconds

Math Notermans

Christoph Krieger this is exact the same method Sam already uses. The Date Object in your code uses the global date to parse the time elapsed. Although this works fine it only shows the scope issue thats underlying to all triggers in Storyline. The SetInterval method in this code only works because it uses the global Date Object.

Christoph Krieger

Here is an quick and dirty example of global countdown made with javascript:

https://malziland.at/articulate/countdown_global_js/story.html

After publishing:

  • Open .story file with a text editor and add the following line into the head:

<script src="scripts.js"></script>

  • Copy the scripts.js into the output folder
Math Notermans

Yeah the solution is fine. Only thing is that Sam already had that solution a few years back as he mailed me ;-)

Only thing  i dislike is adding code time after time in the index file when publishing. Using a WebObject to inject it into the html would be better. Or edit the index file in Articulate Programmes folder then you only have to do it once.

Jeremy Trott

After about 15 hrs of chatgpt helping me write code I've got something that works!

 

function zeros(num) {
  if (num < 10) {
    return "0" + num;
  }
  return num;
}

var player = GetPlayer();
var count = 20; // Set the countdown duration to 20 seconds
var timerID = player.GetVar("Timer_ID"); // Retrieve the stored timer ID

function startTimer() {
  timerID = setInterval(timer, 1000);
  player.SetVar("Timer_ID", timerID); // Store the timer ID in a Storyline variable
}

function stopTimer() {
  clearInterval(timerID);
}

function resetTimer() {
  stopTimer(); // Stop the previous timer
  count = 20; // Reset the countdown duration to 20 seconds
  var minutes = zeros(Math.floor(count / 60));
  var seconds = zeros(count % 60);
  var totalTime = minutes + ':' + seconds;
  player.SetVar("Countdown_Display", totalTime);
  player.SetVar("Timer_finished", 0);
  startTimer(); // Start the new timer
}

function timer() {
  if (player.GetVar("Reset_Timer") === 1) {
    resetTimer();
    player.SetVar("Reset_Timer", 0); // Reset the player variable
    return;
  }

  count = count - 1;
  var minutes = zeros(Math.floor(count / 60));
  var seconds = zeros(count % 60);

  var totalTime = minutes + ':' + seconds;
  player.SetVar("Countdown_Display", totalTime);

  if (count <= 0) {
    player.SetVar("Timer_finished", 1);
    stopTimer(); // Stop the timer when countdown is finished
  }
}

// Clear the previous interval timer if the stored timer ID is valid
if (timerID) {
  clearInterval(timerID);
}

// Initial setup
resetTimer(); // Call the resetTimer() function initially to set up the countdown

// Example usage: Set the player variable "Reset_Timer" to 1 to reset the timer
player.SetVar("Reset_Timer", 1);