Displaying random elements for a quiz app I'm a teacher and I built a one-page web app to display random question sets for quizzes and tests. The script I wrote selects one question from a set (array) of questions. 

The script works fine, but I wonder if it follows best practices or if it could be made more efficient. I want to share my app with other teachers, but I don't want to spread bad practices.

Here's a fiddle of the app itself.

Clicking anywhere on the page brings up a spinner. Clicking again brings up five new random questions. There is more JS in the app, but I'm mainly concerned about the JS related to the question randomization.

// change questions here -- in quotes, comma separated
var qset1 = new Array("Set 1 Question 1","Set 1 Question 2","Set 1 Question 3","Set 1 Question 4","Set 1 Question 5");

var qset2 = new Array("Set 2 Question 1","Set 2 Question 2","Set 2 Question 3","Set 2 Question 4","Set 2 Question 5");

var qset3 = new Array("Set 3 Question 1","Set 3 Question 2","Set 3 Question 3","Set 3 Question 4","Set 3 Question 5");

var qset4 = new Array("Set 4 Question 1","Set 4 Question 2","Set 4 Question 4","Set 4 Question 4","Set 4 Question 5");

var qset5 = new Array("Set 5 Question 1","Set 5 Question 2","Set 5 Question 5","Set 5 Question 4","Set 5 Question 5");



function setUP() {
var set1=Math.floor(Math.random()*qset1.length);
var set2=Math.floor(Math.random()*qset2.length);
var set3=Math.floor(Math.random()*qset3.length);
var set4=Math.floor(Math.random()*qset4.length);
var set5=Math.floor(Math.random()*qset5.length);


document.getElementById('set_1').innerHTML = qset1[set1];
document.getElementById('set_2').innerHTML = qset2[set2];
document.getElementById('set_3').innerHTML = qset3[set3];
document.getElementById('set_4').innerHTML = qset4[set4];
document.getElementById('set_5').innerHTML = qset5[set5];

}
I assume your sets won't actually look like that, and that was just for the sake of this example.  Right now you're doing the same operation on a bunch of virtually identical things - this is exactly the use case for a for-loop.

First we need to put all of our questions into an array of arrays, and then we can loop over them and do whatever we want.  I made a few other changes as well - by using the nth-child() selector I don't have to have the ids - you can switch between the two methods by changing which lines are commented out.  You can run this code snippet and see how it works down below.



function populateQuestions() {
  var questionSets = [
    ["Set 1 Question 1", "Set 1 Question 2", "Set 1 Question 3", "Set 1 Question 4", "Set 1 Question 5"],
    ["Set 2 Question 1", "Set 2 Question 2", "Set 2 Question 3", "Set 2 Question 4", "Set 2 Question 5"],
    ["Set 3 Question 1", "Set 3 Question 2", "Set 3 Question 3", "Set 3 Question 4", "Set 3 Question 5"],
    ["Set 4 Question 1", "Set 4 Question 2", "Set 4 Question 4", "Set 4 Question 4", "Set 4 Question 5"],
    ["Set 5 Question 1", "Set 5 Question 2", "Set 5 Question 5", "Set 5 Question 4", "Set 5 Question 5"]
  ];

  for (var setIndex = 0; setIndex &lt; questionSets.length; ++setIndex) {
    var questionSet = questionSets[setIndex];
    var questionIndex = Math.floor(Math.random() * questionSet.length);
    var question = questionSet[questionIndex];
    var selector = '#questions li:nth-child(' + (setIndex + 1).toString() + ')';
    //var setId = 'set_' + (setIndex + 1).toString();
    document.querySelector(selector).innerHTML = question;
    //document.getElementById(setId).innerHTML = question;
  }
}
&lt;ul id="questions"&gt;
  &lt;li id="set_1"&gt;First question&lt;/li&gt;
  &lt;li id="set_2"&gt;Second question&lt;/li&gt;
  &lt;li id="set_3"&gt;Third question&lt;/li&gt;
  &lt;li id="set_4"&gt;Fourth question&lt;/li&gt;
  &lt;li id="set_5"&gt;Fifth question&lt;/li&gt;
&lt;/ul&gt;
&lt;button type=button onclick="populateQuestions()"&gt;Populate the questions!&lt;/button&gt;




I do have one potential concern - right now you're still presenting the sets in the same order, but you might want to randomize what order the sets appear in. I'll leave that as an exercise to the reader, but it isn't much more complicated than this. You might also want to not present questions that have already been seen before, which is a little tricky. You either want to remove them from the sets once they've been used, or have an object to keep track of ones that have already been used.

Lastly, it might be worthwhile to write a function like getQuestions that looks like this

function getQuestions(callback) {
    // do something to get the questions
    var questions = ...;
    // once the questions are ready, use the callback
    callback(questions);
}

function populateQuestions(questionSets) {
    // same code, minus the creation of questionSets
}

getQuestions(populateQuestions);


We use the callback so this can become an asynchronous method - if you ever wanted to get this data via AJAX, or a web-service, or database, or wherever, it becomes much easier to integrate that into your workflow.