Populate a drop-down list with javascript

How can I populate a drop-down list with javascript in Lectora?

I need to allow learners to tell me which of our 230+ stores they work in. The list of stores changes as we open new ones, and I don't want to have to edit and republish the course every time. So, I have the list in an external file. I know how to read the file. I have a javascript that can pop up an alert showing each store number as it iterates the array. But I cannot figure out how to I add the elements of the array to my drop-down box.

Outside of Lectora, I do this, and it just works:

select = document.getElementById( 'stores' );

for( store in stores ) {

select.add( new Option( stores[store] ) );

}

Inside Lectora, I do this, and nothing -- no error, empty drop-down list:

select = combo1609.objLyr.ele;

for( store in stores ) {

select.add( new Option( stores[store] ) );

}

Am I not setting select to the drop-down list correctly? Is there a hidden language difference that means I need to do something other than select.add( new Option ... to add an option to a drop-down list? Am I doing something else wrong?

Discussion (23)

Maths,

This wasn't exactly the solution I needed, but it gave me the clues I needed. Thank you very much!

First, I was indeed not correctly identifying the control. Silly me for thinking I could find the element ID within the .awt instead of previewing in a browser and looking at the source.

Second, this bit that I was trying originally does not work in Lectora, for reasons I don't understand but no longer care about:

for( store in stores ) {

select.add( new Option( stores[store] ) );

}

My challenge with your solution was that it required the drop-down list to have the correct number of elements to start with, and that will change for me, growing by one every time we open a store. But this works perfectly:

var droplist = document.getElementById("combo1069id");

var opts = stores.length;

droplist.options.length = opts;

for (var i=0; iundefined

Hey Stan,

Shared my solution to this here:

http://community.trivantis.com/shared-content/fill-stans-drop-down-list/

Just plain old vanilla javascript. Works fine.

I do think that maybe the only thing that you missed in your solution was the proper id of the dropdownList.

As you see in my sample the name to refer is combo39id...

You could give that a try, or just dissect my sample.

Kind regards,

Math

Hi Stan,

I don't have an answer, unfortunately. However, I do know that when I've populated Lectora screen elements from custom javascript I've needed to examine closely the actual HTML that Lectora produces to discover where precisely I need to do the substitution.

Also, I've found jquery particularly useful for this purpose as it makes it easy to select the specific element you are trying to manipulate. Here is the resource that got me started with that:

http://trivantis.com/blog/using-jquery-lectora-customize-e-learning-courses-part-2

Hope this is helpful.

andrew

Hi Stan, i do think jQuery UI ( http://jqueryui.com/controlgroup/ ) does a great job at that. If im not wrong i have it used in several Lectora projects.

Gonna look for you if i can find a sample.

Kind regards,

Math

Nope, I'm good, thanks! I knew how to resize the droplist.options array, and it works great! Thanks again for your help.

Stan

Yeah, after i uploaded my solution i immediately knew...wait what if... if you cant get it working with a list that hasnt a preset length let me know, and i will jump on it too ;-)

Jason, yes, this is definitely possible. I don't have time to code it right now, but the basic idea would be:

  1. For each option in List A, set up an array with the options to show in List B when the List A option is chosen.
  2. For each option in List B, set up an array with the options to show in List C.
  3. For the Select/Change event on List A, run a javascript with a switch statement to populate List B with the array that matches the option chosen from List A.
  4. Repeat #3 for List B to populate List C.

If you need help with the code itself, post back; one of the real experts might get to you with a coding example, and I'll check back and provide one when I can if no one beats me to it.

Hi Jason, mocked up a quick solution for this. Based on my previous entry for Stan, i continued on that... now linking 3 boxes together filling the content dynamically. Some things could be improved, but well.. one step in the right direction i do think.

http://community.trivantis.com/shared-content/multiple-dynamic-dropboxes/

Regards,

Math

I'm reviving this thread as I want to do something that this may work for with some adjustments.

I'm wondering if it's possible to populate a drop down list based on the selection from another drop down list. Basically, each choice in one drop down list changes the list in a second drop down list. The choice in the second drop down list changes the list in a third. I know I can use a hide/show on Select/Change to show or hide the different drop downs, but if there's an easier way, that would be awesome!

That's exactly the idea - Thanks for the offer of help with the code. The only JS I know is based on what's been shared with me through the forum and some experimenting based off the help. I fall into that category of developers that was originally thrust into the position because I was a trainer, have vision and am very technically inclined. JS and CSS are on my agenda for training once this project is complete.

@smiller7502

if you could show me/us a simple working example using 'switch/case' instead of either the standard 'if/then' or Lectora's conditions would be great. Tried to implement switch/case in my sample but kept bumping into weird issues. Not sure why, and in the end stopped trying ;-) Wondering if it works in a Lectora environment...

@Darrel How Deja vu ? The store issue is fixed in this setup ;-)

EDIT: I just noticed that you have to add selections in the droplist properties equal to the number of choices in the text box. However, I ran into a situation where if there is a difference, there are listings that say "undefined" if the text box has fewer options than the list in the droplist properties. For example:

  • Droplist 1 has 2 items.
  • If you choose list item 1, droplist 2 should only have 1 item.
  • If you chose list item 2, droplist 2 should have 5 choices.

Since I need 5 choices for droplist 2, I put items 1-5 as choices for droplist 2. The problem is that if I choose item 1 from droplist 1, droplist 2 shows 1 correct item and 4 "undefined" items. This happens in reverse, too - with only item 1 in the properties for droplist 2, it only ever shows 1 item.

I also tried removing all choices from droplist 2, but that definitely doesn't work either. So close!

Yeah, the amount is fixed as is. Can be made dynamic though, i do think Stan (@smiller7502 ) had a solution for that, so maybe he can add it. If i got some time, i might change that.

Been busy. Deja Vu because we've done the populate drop-down thing already (yeah, this has a slightly different twist but the main idea is the same).

I would use JSON and my own inputs for this.

I tried looking at both sets of JS just to see if I could puzzle out what each line does to try and find a way to merge the solutions, but hit a bit of a wall with it. I think I understand some of what it's doing but experimenting came up a bit short. Here's what I THINK I was able to figure out:

In Math's script:

  • Lines 1&2 set a variable to use for where to get the values for the list for the second droplist and how they're separated.
  • Line 3 sets a variable for the combo.
  • Line 4 sets a variable for what the length of the list should be(?)
  • Line 5 is where I start to get a little lost, since the line is the same in the script from Math and Stan - this is a loop to determine the number of items in the drop list and sets "i" to equal the number of items in the text box?
  • Line 6 (I think) determines the allowed number of options in the list
  • Line 7 populates the droplist with text for each option

Am I close? I THINK the difference is that Math is using the number of items prepopulated in the droplist in Lectora ("var opts = the HTML name of the droplist") to set the value for the length, whereas Stan is using a "stores.length" to define the value for "opts". If I know the number of options in each droplist, can I just define "opts" as that number?

undefined

At least with explaining my part of the script your quite close. I am not sure about Stan's. His solution for making dropLists dynamic isnot included in this thread i think.

Line 5-7 is a loop. Looping through the array and filling the options for the dropdown both as value and in Html.

And yes my setup has a preset droplist with fixed amount of items. To make it work with a dynamic amount you need to 'add' the proper dropdownList with the specific amount of items.. something like this.

http://jsfiddle.net/adriancarriger/m5mgqp0x/

Regards,

Math

Sorry I haven't had time to pay much attention to this yet, Jason. I can offer a little something, though. Here's the relevant part of the script I'm using, starting from Math's suggestion:

var droplist = document.getElementById("combo1069id"); //1. Identifies the list box to fill.

var opts = stores.length; //2. Determines the length of the array that will fill the list.

droplist.options.length = opts; //3. Sets the number of options in the list box to the number of items in the array.

for (var i=0; iundefined i++){ //4. Loops through the array.

droplist.options[i].value = i; //5. Sets the value of this element of the array.

droplist.options[i].innerHTML = stores[i]; //6. Sets the display text for this element of the array.

}

I found that without line 3, some browsers choke on line 4 when it tries to set the value for an element that doesn't exist yet. I also had no luck getting the script to simply add an element in each iteration.

To answer your most recent question, yes: If you know the length of the array ahead of time and are confident it will never change, you can hard-code "opts" to that value. This requires that every choice in List A lead to the same number of choices in List B, and that every choice in List B leads to the same number of choices in List C. In short, hard-coding the value imposes a potential future cost (it makes the code less robust), and I do not see a corresponding benefit.

undefined

Thanks, guys! @smiller7502 - One place I got stuck was on the "stores.length" and what that represented, as it's only in one place in your script. I agree that hard coding the value is not the optimal solution and thought it would be a stop gap until I work this out.

The way mine would work is that option 4, 5 and 6 in list 1 result in different values in list 2, which then can lead to variable lists for list 3. I'm going to go on record by saying that software simulation is a PAIN, but not because of Lectora.

Yeah, the reference to stores.length was definitely confusing, sorry. The stores[] array is created and populated in a separate javascript.

Ah, that makes sense now and was what I thought. Through research, I believe I've found the code that will create the array, which looks pretty straightforward (setting a variable to equal the list with text in quotes, separated by commas). Would I be right in saying that all of the code is in the external JS file, then attached via a meta tag in the course?

I think once I am able to do this, my next barrier is how to edit the JS file without republishing the course to our LMS. We use Pathlore, which is new to me, so I'm not sure how much access I have to the actual files where I could either edit-in-place or just edit locally and replace.

Here's the rest of my code. The examples may be more complicated than you need, because each element in the external array is a comma-delimited list that tells me a lot of things about each store: its state, market and region, whether it's represented by a union or not, whether it has a car wash or not, and its the minimum age for purchasing tobacco products in that store. This adds some complexity to the code but makes both the code and the external list re-usable in other courses.

Another complication for the same reason: On line 7 you'll see a call to indexOf, which locates the column header I want instead of assuming that it's always the third column or always the seventh column or whatever. That way, if we come up with another piece of data that would be useful in the external list, we can add a column anywhere, wherever it makes sense while managing the list. For instance, if the company decides sometime in the future to add a layer between market and region, it would be confusing to anyone editing the list to have state, market, region and then five columns later have subregion. Making the code find the heading it needs allows the list to maintain logical structure without breaking existing uses of the list. (I don't know if I've explained that very well or not. If not, call me out on it.)

HTML extension, type Meta tags, in the AU:

undefined //1. Include the external list.

undefined //2. A new script.

document.addEventListener("DOMContentLoaded", function(event) { //3. Wait for the document object model to fully load. Without this, some browsers will throw an error because the script tries to refer to a drop-down list before the list exists.

//CHANGE TEXT BETWEEN QUOTES TO THE HTMLNAME OF YOUR DROPDOWN + "id"

var unitlist = document.getElementById("combo26449id"); //4. Set the variable "unitlist" to the drop-down list control.

var fldHeading = [] //5. New array for column headings.

fldHeading = units[0].split(','); //6. Split the first element of the units array (a row of column headings) into the fldHeading array.

var idxDescr = fldHeading.indexOf("Description"); //7. Find the Description column (the one I want to show in my drop-down list).

var opts = units.length; //8. Get the length of the units array.

unitlist.options.length = opts; //9. Set the number of options in the drop-down list to the number of elements in the units array.

var fldData = [] //10. New array for data from the units list.

for (var i=0; iundefined i++){ //11. Loop through the units array.

fldData = units[i].split(','); //12. Put the next element of the units array into the fldData array.

unitlist.options[i].value = i; //13. Set the value for this option.

unitlist.options[i].innerHTML = fldData[idxDescr]; //14. Set the text for this option.

}

});

undefined

And then here's what runs when the learner selects an element from the drop-down list. This is an On SelectChange Run JavaScript action on the page that contains the drop-down list:

var unitlist = document.getElementById("combo26449id"); //A. Identify the drop-down list again. Probably there's a way to scope this so I'd only have to do it once, but I haven't foundthe way yet.

var selected = unitlist.selectedIndex; //B. Set the variable "selected" to the element the learner picked.

var fldHeading = [] //C. Same as #5 above.

fldHeading = units[0].split(','); //D. Same as #6 above.

var idxStore = fldHeading.indexOf("Description"); //E. Same as #7 above.

var idxTobacco = fldHeading.indexOf("TobaccoSalesMinAge"); //F. Similar to E and 7, just finding a different column.

var fldData = [] //G. Same as #10.

fldData = units[selected].split(','); //H. Same as #11.

VarStore.set(fldData[idxStore]); //I. Set the Lectora variable "Store" to the description the learner picked.

VarTobacco.set(fldData[idxTobacco]); //J. Set the Lectora variable "Tobacco" to the TobaccoSalesMinAge value for the store the learner picked.

Discussions have been disabled for this post