Disclaimer: The techniques in this article access areas of the Storyline JavaScript output which are neither documented nor officially supported.

Have you ever tried to implement a custom progress bar in your Storyline projects? If you have, then you will be aware of the difficulty of implementing such a feature. It usually involves setting up numerous variables and/or triggers which quickly become a nightmare to maintain when your activity grows and slides are changed. I am going to show a new way of doing this that is completely dynamic and requires no triggers or variables.

After a lot of digging via the Chrome dev tools I was able to uncover a few useful properties and methods that enabled me to create a progress bar.

To get the object for the slide that is currently showing you can use:

player.currentSlide();

You can then use this slide object to get the index of the slide in its scene:

player.currentSlide().sceneSlideIndex; // zero based index

You can also get the index of the scene that the slide belongs to:

player.currentSlide().sceneIndex; // one based index

I have so far been unsuccessful in finding where to get the total number of slides in the activity. But I was able to come up with a function that calculates the number of slides based on each slide object’s sceneIndex property. You can access all the slide objects in the story object on the window like so:

story.allSlides; // array of all slides

The function to calculate the number of slides for a given scene looks like this:

function getSlideCountInScene(scene) { 
  var scns = {};

  for (var i = 0; i < story.allSlides.length; ++i) { 
    var s = story.allSlides[i]; 
    var sx = s.sceneIndex; 
    if (!scns[sx]) { 
      scns[sx] = 1; 
    } else { 
      scns[sx] += 1; 
    } 
  } 
  return scns[scene] || 0; 
}

This function sorts all the slides in the activity into scenes and returns the number of slides for the scene index it was given.

We now have all the information required to make a progress bar – the current slide and the total slides:

// add 1 so it is a one based index like totalSlides will be 
var currentSlide = player.currentSlide().sceneSlideIndex + 1; 
var totalSlides = getSlideCountInScene( player.currentSlide().sceneIndex ); 
// percentage from 0 - 1
var progress = currentSlide / totalSlides; 

The final thing we need to do add a visual representation of the progress bar to the player. This is actually the easiest part, since it’s all HTML we can just use jQuery (which is already included by Storyline) to append a couple of <div>s to the player and size them based on our calculated progress.

We will create a global variable called progBarAdded to check whether the progress bar has already been added to the player. This will ensure that the progress bar is only added once and not on every slide.

First we need to check if it is not set to true, and if it’s not true we will set it to true and then add the progress bar:

if (!window.progBarAdded){ 
  window.progBarAdded = true; 
  //...add the progress bar and style it 
}

Assuming you have some player buttons in the bottom area of the player we can add the progress bar to the controls div using jQuery:

$("#controls").append("<div id='x-progress-bar'><div id='x-progress-inner'></div></div>");

This adds a single div containing another single div. The outer div is what we’ll use to position and size the bar, and the inner one will be the progress fill. Next we have to style the divs using jQuery’s css method.

Set the position, size and style of the outer div:

$("#x-progress-bar").css({ 
    "display" : "block", 
    "width" : "150px", 
    "height" : "16px", 
    "background" : "#FFF", 
    "border" : "1px solid #2dbf2a", 
    "border-radius" : "6px", 
    "overflow":"hidden", 
    "position" : "absolute", 
    "top" : "5px", 
    "left" : "0" 
});

Set the inner div size and style:

$("#x-progress-inner").css({ 
    "width" : "1px", 
    "height" : "100%", 
    "background" : "#2dbf2a", 
    "position" : "absolute", 
    "transition" : "width 0.3s ease-in-out", 
    "top" : "0", 
    "left" : "0" 
});

After adding the progress bar divs to the player and styling we just need to update the width of the fill based on the progress. This happens after the IF block so that it is run on every slide:

$("#x-progress-inner").css("width" , (currentSlide / totalSlides * 150) + "px"); // 150 is the width of the outer div

Putting all of this together – with a little bit of tidying up – we end up with the following chunk of code:

var xw = 150; // width of progress bar
var xh = 16; // height of progress bar
var slideScene = window.player.currentSlide().sceneIndex + "";
var totalSlides = getSlideCountInScene(+slideScene);
var currentSlide = window.player.currentSlide().sceneSlideIndex + 1;

if (!window.progBarAdded) {
  window.progBarAdded = true;
  $("#controls").append(
    "<div id='x-progress-bar'><div id='x-progress-inner'></div></div>"
  );
  $("#x-progress-bar").css({
    display: "block",
    width: xw + "px",
    height: xh + "px",
    background: "#FFF",
    border: "1px solid #2dbf2a",
    "border-radius": "6px",
    overflow: "hidden",
    position: "absolute",
    top: "5px",
    left: "0",
  });

  $("#x-progress-inner").css({
    width: "1px",
    height: "100%",
    background: "#2dbf2a",
    position: "absolute",
    transition: "width 0.3s ease-in-out",
    top: "0",
    left: "0",
  });
}

$("#x-progress-inner").css("width", (currentSlide / totalSlides) * xw + "px");

function getSlideCountInScene(scene) {
  var scns = {};
  for (var i = 0; i < story.allSlides.length; ++i) {
    var s = story.allSlides[i];
    var sx = s.sceneIndex;
    if (!scns[sx]) {
      scns[sx] = 1;
    } else {
      scns[sx] += 1;
    }
  }
  return scns[scene] || 0;
}

Now you may be wondering where to put all this code. It needs to be run on every slide so the best place to put it is in an execute JavaScript on timeline start trigger on the main Master Slide. That way every slide will automatically inherit it and you won’t have to worry about you or someone else forgetting to add it on every slide.

You should now have a progress bar that displays your users’ progression without any additional work after this initial set up. It will automatically adjust whenever you add or remove slides – you won’t have to think about it anymore.

The bar will show the progress in the current scene, so if you are in slide 10 of 10 in scene 1 it will show 100%, then if you move to slide 1 of 10 in scene 2 it will go back to showing 10%. It will not show the progress of the overall activity – that might be a topic for a future post!