Global scope for Javascript variables in Storyline

May 02, 2022

As Storyline publishes all code and variables to separate functions in the user.js file the scope for individual variables is local. The solution i show here creates a global scope for functions and variables so you can reuse your Javascript code and variables anywhere in a Storyline project.

Basically i make use of a WebObject to add an external Javascript to Storyline. In that script variables are stored globally so you can reuse them anytime. Also with this solution you can add any custom function to the globalScript.js file. Those functions can then be called from any spot in Storyline.

Lets see it working first.
g1
https://360.articulate.com/review/content/d02ea9a4-b779-4447-befa-7af5a2d02e21/review
In this Storyline you can enter 2 values. A property name and its value. Those will be saved in a Javascript object and can be called with a global function or directly. When adding your first one... it shows in the textfield on the left... then you can continue to the next slide...and do the same... either edit one of the added values...or add a new one.

The function iterateObject(_obj,_desiredoutput); you can use to get your variables and values. Offcourse the argument _obj is for your Object in Javascript. In this case  its 'globObj', but you can add as many as you want and customize them. The argument _desiredoutput is for what you want returned. Default that is a string, but you can choose to get an array.

Alright some more info on how it works. As you can see in the Storyline file i use a separate scene for the WebObject that loads the Javascript file(s).
g3
As you can see i use subfolders for my Javascript files. Nothing more then the empty index.html and the globalScripts.js in its own subfolder for the WebObject.
g4
To load the Javascripts files into Storyline i use a Javascript trigger on the start of the timeline. This is probably best added to a Master Slide and ensure its only loaded when the variable 'javascriptsLoaded' is False.
g6
When changing anything to the 'globalScripts.js' or the WebObject you would need to change 2 variables in the script. amountOfLibs and webObjectURL. The latter offcourse refers to the folder Storyline publishes the webObject to. And you would need to publish and check the name of the folder when changing anything.
g7
Then you can call the functions from the 'globalScript.js'. In this sample i only added 2 functions. One to set the variables and one to get them again. Both work on any spot in the Storyline. After the Javascript is loaded you can set and get variables from anywhere in your Storyline.
g8
Do hope this is usefull for some of you. 

22 Replies
Jürgen Schoenemeyer

if you wan't to use small solutions for global variables in javascript - use window-variables (storyline is technical a single-page application)

https://javascript.info/global-object

example

javascript trigger 1:

function hallo(){
window.myText = "hallo";
}


javascript trigger 2:

function printHallo(){
console.log( window.myText );
}

normaly window variables are not used*, the normal javascript would be

var myText;

function hallo(){
myText = "hallo";
}

function printHallo(){
console.log( myText );
}

the result is the same, but this it is not possible in a trigger javascript

Jürgen

* as a developer you should not use such things - global variables are nasty. If you are not careful, they can cause big problems. Normally you encapsulate variables. 

steve paradis

Hello Guys,

My personal solution is to use a webObject on the first scene, with sessionStorage.

I do load a webObject (first slide and off stage). the code in the index.html is simply :

*************************************
<script>
sessionStorage.setItem("msg_EN","Hello world!");
sessionStorage.setItem("msg_FR","Bonjour le monde!");
sessionStorage.setItem("msg_ES","Hola mundo!");
</script>
*************************************

These become available for each slides even for other webObjects :

var player = GetPlayer();
player.SetVar("greetings",sessionStorage.getItem("msg_EN"));

If you care about making the variables more "private", you can always use base64 encode/decode, here is the function I use:

****************************************
function encodeB64(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}

function decodeB64(str) {
return decodeURIComponent(atob(str).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}

Emmanuel Nédélec

Hi Math,

I'm having hard trying to to make this work, I don't get the console.log messages with my work, while it does appear with your example.

How do you figure the value of webObjectURL ? Do you publish a first time to see the name of the folder the publication process creates, and then rewrite it in the first javascript executed in the first slide, or is there any other way to get that name ?

Emmanuel Nédélec

This is really weird.

It doesn't work when I try to build it from scratch, even copy-pasting your slides, or objets and scripts (I do pay attention to the webObjectURL value that changes for each project).

If I take your example but recreate the webOject to point to my version of the .js file, it still works.

If I then remove the 2nd slide of your example, and then paste two of my slides, it doesn't work anymore, the .js file isn't downloaded anymore, I get no more console.log messages. But my 2 slides appear after your "firstSlide", so I don't understand why suddenly their presence would prevent the .js from being loaded.

Emmanuel Nédélec

Ok, thank's.

For the context, I'm trying to find a way to load a 4 dimensions array of values, because I am asked to create a kind of quiz that gives scores in 11 different topics.
There are 3 series of questions, each composed of 5 questions, for which there are 4 answers possible, and each answers affects differently 11 scores, all together.

To help the person in charge of writing the questions and deciding wich answer gives how many points in wich score, I created a excel sheet that allows her to write down those points and check what score she gets when she selects what answers. She has to make many tries to precisely fine tune those points values.

In a classic Storyline approach, one would make 15 slides (3 * 5 questions), containg each 4 radio buttons, and therefore the number of triggers associated to them or to a "next" button in each slide would be 44 (4 answers * 11 values to change by answer), so that leads to 660 triggers (15 * 44).

There is no way I create and edit 660 different triggers extremely similar, the only difference betwwen them being an integer value.

So in the excel sheet I created a formula that writes all the values in a 4D js array syntax :
var myScores = [[[[ ........ ]]]];
organized as myScores[series][question][answer][points]

This js array is declared in my modified version of your GlobalScript.js file, in which I also added a simple return score fucntion :

function getScore(serie,question,answer,score) {
console.log("score calling : ser=" + serie+ " , q=" + question+ " , a=" + answer+ " , sco=" + score);
return myScores[serie][question][answer][score];
}

In Storyline, I set at the begining of each slide 2 variables (for declaring which serie number and question number are we playing), the answer number is set by 4 triggers associated to my radio buttons, and finally I use a javascript trigger on my next button that calls 11 times myGetScore function to set my 11 storyline score variables.

Doing so allows me to have exactly the same triggers in each slide, except for the starting one that declars which question of which serie are we in, but that I don't have to change whatever changes are made to the score matrix.

Each time my workmate will need to have a new version of the quiz with new point values, all I would have to do is edit the globalScript.js file in order to paste in it the result of the excel formula (lazy guy spotted, I agree), and republish after having re-set the webObjectURL value : 2 paste actions instead of 660 clic-edit is the kind of thing that makes my day :-D

Explaining all this and preparing my file in order to share it here lead me to better results : I do now see console messages from globalScript.js initialisation lines, but as soon as I call my getScore() function, nothing happens.

Math Notermans

So testing your file...and disabling the next button on the 2nd slide i nicely get this console.log...

Appel de score : sit=1 , quest=1 , rep=1 , comp=1
val= 1 | valeur prec de sRes=0

On the suite button i changed the console.log to this...
console.log("val= "+val+" | valeur prec de sRes=" + prec);

So apparently ( correct me if im wrong ) you get all proper data from your multidimensional array and all logs fine.

I suspect that Storyline goes to quick to the next slide, so i changed the nextSlide trigger to waiting for a new variable to change: nextSlideAllowed. Default it is false, thus atm not going to the next slide. Im gonna see whether i can set that variable to true, thus going to the next page and whether all works then.

Emmanuel Nédélec

Thank you so much, I'm going to look closely at this variable state change. I confirm that the message "appel de score" is from inside the js function that returns the wanted score value.

The last slide is actually totally temporary. In the end it will display the 10 scores using 10 sliders objects that I make non-interactive by placing an invisible button above them, and that I preset whit the score variable value.

Emmanuel Nédélec

For anyone reading this in the future : In addition to what Math spotted (the "go to slide" instruction that prevents previous javascript call) many of my difficulties here were because I had several errors in my script that immediatly disables all javascript in the project. The slightest missing ";" at an end of line can cause this.

In my case, I used also "let" declaration for a temp variable I wanted to declare & initialize several times in the same javascript call. At the very moment a 2nd "let" line for the same variable appeared in my script, nothing worked anymore.

I didn't yet sort the "Storyline goes too quick to the next slide" problem properly : actually I stop the chronology of the current slide, and when the user clicks "next" button I call my js fucntions and let the slide carry on : it ends 1 second later, with automatic go to next slide. And this seems enough for letting js call to be done.

I have a last question @Math : can your solution work with a 4D array as mine ? I would like to try to initialize my array from within a javascript inside my storyline project rather than from globaScript.js file, that would prevent the WebObjectURL value to change each time the content of my array has changed.

Math Notermans

Just made a sample showing proper workflow for creating multidimensional arrays and gettting the data from it.
https://360.articulate.com/review/content/7c1e0e02-77c8-4bb5-a632-21c21d10f7f3/review

Clicking the create multi array button creates a 3-dimensional array. 
You can change the inputs for the depth of the array and the 2 inputs from 0-2 showing a value in the appropriate cell.

multis
Clicking show value from array shows the appropriate value from the multidimensional array.
In fact clicking either button will create that multidimensional array from the variable showing... 'cows,horse,monkey,grass...' etc. As this variable now is only 9 items long, changing the dimensions of the multiarray to more then 3 will result in getting 'null' as the cells will be empty.

But basically this has all to create your own multidimensional arrays.

Kind regards,
Math