I love the Articulate eLearning Hero challenges. What I really love however is when a particular challenge (like the one that was ran this week) poses its own internal challenge based around the standard question of ‘wouldn’t it be great if Storyline did X’.
One of the great things about the software is that even just with the base tools we are still discovering ways of performing actions that, up to now, we didn’t realise were actually possible, but the fact that Storyline also allows for JavaScript code to be attached to any specific on slide action or event also opens up so many extra opportunities.
It actually takes me back to my old days of working with the content development team at RWE npower, with whom we would often have a discussion which started with that exact question! Something along the lines of ‘Nokia Snake in Storyline, how would you do it?’. As a matter of fact it was that very question which led me to actually build the game in SL360! As you can see here: https://discoverelearninguk.com/projects/snake-storyline-360/
This week the question on the Articulate forum was being set by Jonathan Hill, who observed that the ability for the browser to recognise the specific position of the Scrolling Panel slide object would open up so many doors in terms of the types of interaction that could be achieved. I totally agreed, and after some thought and help from the community (special thanks to Preetha!) I designed my own submission to the challenge based on that functionality using JS.
However I couldn’t stop there, because this wasn’t actually my original idea for the challenge this week. What I really wanted to do was something like an X-Ray machine, which would involve using two layered Scrolling Panel objects in order to create the illusion of the skeleton layer over the top of the main character. What I couldn’t account for at the time was how one Scrolling panel could be linked to another without it being fired away up the screen the moment the top panel was moved. They needed to remain their own separate objects, but share a common value that determined their specific Y-Axis value.
Once I had the answer to how to get each of the scrolling panel position values themselves stored as a unique variables in JavaScript though, this was the spark that lit the bonfire in my mind. I then set to work on creating what you see here:
So now I would like to take you on a deep dive through the code itself that makes this interaction work, in the hopes that it may be helpful to others some day. I’m also going to split everything up to make this as easy to follow as possible for Storyline developers who may not be used to JS code. To understand the absolute fundamentals of programming and working with code, I highly recommend this easy to digest Lynda.com course – https://www.lynda.com/Programming-Foundations-tutorials/Foundations-Programming-Fundamentals/83603-2.html
Capturing The Scroll Area And The Scrollbar That Controls The Scrolling Panel As Variables
The first thing to say is that all JS code you are about to see is all contained in the same ‘Execute JavaScript’ action in Storyline when the timeline starts for the project.
Our first block of code looks like this:
var x = document.getElementsByClassName("scrollarea-area"); var y = document.getElementsByClassName("scrollarea-scrollbar");
The getElementsByClassName method in JavaScript allows us to capture all instances of a specific class that exist within the document (ie. the published output) and store these within a JS variable for referencing in future functions. The fact that we are gathering ‘Elements’ (plural) means that we could be capturing more than one instance of a specific class in the document as an ‘array’.
What this allows us to do next is to apply functions that would affect all or one of these specific elements, which we can do by denoting the array value of the instance we want to target. Arrays always start from the number 0 and work their way up. In the case of my own document, the main Scrolling Panel that the user will interact with will be stored with the value [0], the the X-Ray window which is non-interactive will be stored with the value of [1].
Now we are ready to start building our functions to tell JavaScript what to do with these variables.
IF One Scrolling Panel Moves THEN Move The Other
This actions is achieved with the following code:
x[0].onscroll = function(){ $(x[1]).scrollTop($(x[0]).scrollTop()); var height = x[0].scrollHeight - x[0].offsetHeight; var player = GetPlayer(); player.SetVar("num",x[0].scrollTop/height*100); player.SetVar("height",height); };
Let’s break it down line by line:
- We define what triggers the function, in this case we are saying we want this to happen when the variable x[0] (our main Scrolling Panel) is ‘scrolled’.
- Here we are telling our function that the value ‘scrollTop’, which defines and sets the value of the Scrolling Panel itself, should be the same for both of the panels at all times during the scrolling action.
- Here we create and set a new variable based on the height of the Scrolling Panel itself, taking into consideration the elements padding and scrollbar space. We will use this later to help create our Scroll position value
- Standard Articulate Storyline JS allowing us to move variable data to and from Storyline.
- Here we set a variable with the name of ‘num’ in Storyline with the Y position of our Scrolling Panel (x[0]). The maths calculates this position based on 0 (scrollbar fully at the top) and 100 (scrollbar right at the bottom)
- This is optional, if you want to see the height value on the stage in Storyline use the name ‘height’ for a new number variable in SL.
What About If I Need The Ability For One Scrollbar To Control The Other And Vice Versa?
Go ahead and add the following block of code:
x[1].onscroll = function(){ $(x[0]).scrollTop($(x[1]).scrollTop()); };
It’s essentially the same as above, but switching the numbers around so that we apply the function to the x[1] element (second Scrolling Panel) as well.
Making The Project Responsive To Screen-Size Changes
This is important because the project could potentially be loaded on to any unknown screen size and orientation. There is also the possibility that the user could resize the browser window mid activity (starting small and going full screen for example). So we need to be able to account for this:
$(window).resize(function(){ var newHeight = x[0].scrollHeight - x[0].offsetHeight; var player = GetPlayer(); var height = newHeight; player.SetVar("height",newHeight); });
It is important that we include this code after the block of code above so that it is processed after we get the initial height values from the project when it first loads.
On line one you can see the function is applied to the specific user action when the browser window itself is resized.
The following maths grabs the new Height value much like we did in the last code block, and once this has been stored we then go ahead and replace our existing ‘height’ variable with that new value.
The final step is then passing that back to Storyline in order to show this on screen if we are using the ‘height’ variable as a visible value on screen. As before, this is optional.
Hiding The Scrolling Bar Of One (Or More) Scrolling Panels
$(y[1]).css("display", "none"); $(x[1]).css("-ms-overflow-style", "none");
It is important that we come at this problem with two lines of code due to the fact that Microsoft browsers will not render the Scrollbar object for Scrolling Panels in the same way that Firefox or Chrome does for example. The first line specific targets the ‘scrollarea-scrollbar’ class in the document in order set their display value to “none”. This is good enough for those browsers, however Edge and IE do not use this class, and instead apply their own internal browser scrollbar to the Scrolling Panel objects.
This is why we use the CSS class ‘-ms-overflow-style’ which will target this specific browser-based class in order to also hide the bar in those browsers.
To hide any other scrollbars in the project we would just need to repeat the code and change the array number (eg. [0]) for the specific element that we want to target. For all elements of this class we can remove the array number value and just use the variable reference itself (ie. y).
How Did You Get The Scrolling Panel To Transition In With The Skeleton In Place The Whole Time?
This is actually all controlled within Storyline itself, and was something that was originally discovered by Nejc Žorga Dulmin over at http://masterstoryline.com/. Check out his tutorial to discover how you can create this effect for yourself in your own project: https://www.youtube.com/watch?v=4ZvOtxSDSlA
Is that it?
Indeed it is, barring one more step…
Earlier this year, Articulate decided to remove the reference to the JQuery library from Storyline published content, which means that we have to add it back in before any of this code will actually work.
All you have to do is open up the ‘story.html’ for your project in a code editing software (like Adobe Dreamweaver or Notepad++) and add the following line between the <head> tag (around line 15):
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
As soon as you now open the project locally, or upload it on to your own server for hosting online, everything will be set and ready to go.
Thanks for joining me on this journey as I set about breaking my project apart for this blog post. I hope you find it useful and I will see you again for the next interesting Storyline challenge!