Manipulating the Player Menu with Javascript

Oct 17, 2022

I wanted to share a little Storyline quiz I worked on with a review function and ask a few advanced Javascript questions to see if anyone can help!

Background (if interested): We have a quiz our learners take that pulls 100 questions out of two different question banks (142 questions total). The quiz is timed, and we wanted learners to be able to review questions they need more time on. So I turned on the player menu and enabled "Show Slide Draw Contents" for each of the question banks. Then, at the beginning of the quiz, I use Javascript to hide all the questions from the menu, and each question can be toggled to show in the menu if learners want to return to the question.

Question 1: Here is the Javascript I used for turning off each question in the menu:

let Q1 = document.querySelector("#outline-content > ul > li:nth-child(1) > div");
Q1.style.display = 'none';

let Q2 = document.querySelector("#outline-content > ul > li:nth-child(2) > div");
Q2.style.display = 'none';

// continues in this format all 100 questions (lots of code!)

First, thanks to Math Notermans for pointing me in the right direction to use the document.querySelector to target the list items. My question is, is there a way to use the document.querySelectorAll to target all the li:nth-child divs with just one line of code? Something like document.querySelectorAll("#outline-content > ul > li:nth-child(*) > div");) ... I can't quite figure it out.

Question 2: This is the code I used to toggle each question on/off in the menu (attached to a checkbox):

var x = document.querySelector("[data-slide-title='Here is my question?']");
if (x.style.display === "none") {
    x.style.display = "flex";
  } else {
    x.style.display = "none";
  }

I'm wondering if there is a way to use a variable in the querySelector attribute? That is, in place of the 'Here is my question?', I could use the Project.SlideTitle variable so I don't have to copy/paste each question into that line. Not sure how to write it in there.

Question 3: More of a general question, but does Storyline verify the Javascript of every trigger in the project before running any of it? I ask because I had a delimiter instead of a quotation mark in some code about 50 slides into a project, and none of the code anywhere in the project seemed to work.

If you made it this far, thanks for reading!

5 Replies
Math Notermans

Hi Jordan,

You indeed can use querySelectorAll to get to all elements of a specific type or name.
I have a function in my generic_scripts like this...

function doSomething( _accName ){
  var targetNodeList = document.querySelectorAll("[data-acc-text='"+_accName+"']");
     for(var i=0; i < targetNodeList.length;i++){
        //doSomething for all using a terniary operator as if/else check
        targetNodeList[i].style.display === "none" ? (targetNodeList[i].style.display = "flex") (targetNodeList[i].style.display = "none");
     }
}

Do notice 2 things. querySelectorAll returns a nodeList, so a list of all elements found. So you need a loop to work with them. Selecting one individual in that list is still possible ofcourse.
And i use a terniary operator instead of a normal if/then statement inside the for loop. The terniary operator can replace a if/then statement in one line.

On Question 3... Yes Javascript is picky on spelling errors. One mistake and it won't work. Storyline compiles all your code allover the project in a user.js file. In the development tools in the browser you can easily debug any mistakes. When an error occurs the console in the browser will give a line number in the user.js file where the mistake is found. 

When you donot have errors, you can just click on user.js on the right and the console shows you the line where that is happening. I do think that when you have an error in your code clicking doesnot work and you have to open the tab 'application', find your user.js file...open it up in the console and find the linenumber mentioned. Then you  can quickly see what error you made.

console

Math Notermans

In my back of my mind there was a whisper...this will not work... the solution i suggested... so i made a quick test and indeed my inner voice was correct.

querySelectorAll normally works fine for selecting elements, but in this case you need a child of an element selected and even tougher...the nth-child(1) where the number is going up. Probably to 100 in your case.

I however knew i figured this out before and luckily i have a big library of Javascript snippets to use and reuse meanwhile so the solution was found quickly.

We need a loop over the nth-child.

So i changed my function to this.

switchStyle(3);

function switchStyle( _amount ){
        for(var i=1; i <= _amount;i++){
            // as we need nth-child(1) etc. etc. we need to loop this too
            var el = document.querySelector("#outline-content > ul > li > ul > li:nth-child("+i+") > div");
            //doSomething for all using a terniary operator as if/else check
            el.style.display === "none" ? (el.style.display = "flex") : (el.style.display = "none");
        }
}

So now this is a function to disable all ( or some ) of the menu lists. You just have to pass along the amount of listitems that need to be hidden and it works nicely.

Sample added.

Kind regards,
Math

Math Notermans

And rereading your post...its even simpler.
This will select all nth-children...
li:nth-child(n)

var targetList = "#outline-content > ul > li > ul > li:nth-child(n) > div"
switchStyle(targetList);

function switchStyle( _targetList ){
var targetNodeList = document.querySelectorAll(_targetList);
        for(var i=0; i <= targetNodeList.length;i++){
            var el = targetNodeList[i];
            //doSomething for all using a terniary operator as if/else check
            el.style.display === "none" ? (el.style.display = "flex") : (el.style.display = "none");
        }
}

So 2 methods now in this Storyline to achieve it.
And here you find some great info on selectors for nth-child...
https://css-tricks.com/useful-nth-child-recipies/


Bonus of this approach is you donot need to worry about amount of menu list items.

Kind regards,
Math