Javascript quiz (allows user multiple attempts)

2.4k views Asked by At

I am trying to create a multiple choice quiz. If the user selects the correct answer they are shown an alert saying "Correct" and the quiz moves on to the next question. If the user selects an incorrect answer they are shown an alert saying "Incorrect". This is where I want the quiz to give the user one last attempt before moving on to the next question. I have tried using a variable "tries" to keep track of how many times a user has attempted a question. Currently, the code successfully proceeds to the next question if the user selects the correct answer, but if an incorrect answer is chosen, the "Incorrect" alert pops up and the quiz proceeds to the next question anyway.

I have an external 2-D array that currently stores all the questions and their respective multiple-choices and solution.

renderQuestion() takes the corresponding choices to each question and writes the html that will display the question to the webpage.

checkAnswer() goes through each choice until it finds the choice the user selected. If it's correct, it increments the correct counter and if it's incorrect I want it to tell the user they are "Incorrect" and for the quiz to give the user 1 last attempt on the question before proceeding to the next one.

Here is a condensed version of q_list.js (where I externalized the 2-D array holding all the questions and their multiple-choices):

******EDIT******

We are now trying to include questions where the user has to click on a certain part of a diagram to get the answer right. So now the quiz has both questions that are multiple-choice AND questions that could present the user with a diagram where they have to click on a certain part. There are "mc" and "diagram" identifiers included with each question in the 2-D array to distinguish which type of question the program is handling. Depending on the type of question, the program calls a function that handles multiple-choice questions and one that handles diagram questions. Right now, we are using image maps and are just sending an alert depending on where they click. Is there a way to record where the user clicked (like set some boolean flag selected = true; somewhere?) We also want to have a submit button so the user can select multiple areas of the diagram and and then click "submit" to send their answer.

note: sorry, I don't know how to attach an img file with this code so if you try to run it, it won't work/ you won't be able to see the image we're trying to work with on question 4.

var questions = [

["mc", "A 25 year-old man named Tommy is a football superstar. Throughout his football career, he’s had several concussions. On a crisp fall eve, Tommy rushes into the ER after being checked by an opponent.  Tommy presents with aphasia, dilation of his left pupil, and nausea. Where’s the damage in his brain?", "Right hemisphere occipital", "Bilateral frontal lobe", "Basal ganglia", "Cerebellum", "B"],

["mc", "A patient has trouble making decisions and refraining from saying inappropriate comments. Where could the lesion be located?", "Occiptital lobe", "Temporal cortex", "Parietal cortex", "Prefrontal cortex", "D"],

["mc", "A patient has conduction aphasia. He cannot produce appropriate responses to heard communication, and has difficulty naming pictures and objects. Where is his lesion located?", "Broca's area", "The arcuate fasiculus", "The primary auditory cortex", "Wernicke's area", "B"],

["diagram", "Can you click on the ponytail?", "img/facesmall.png"]

];

    <h2 id="test_status"></h2>
    <div id="test"></div>

    <script src="js/orgNervSysq_list.js"></script>
    <script type="text/javascript">
      var pos = 0, test, test_status, question, choice, choices, chA, chB, chC, chD, correct = 0; 
      var tries = 0; //keeps track of how many times student attempts problem
      var type = ""; //type of question
      function _(x) {
        return document.getElementById(x);
      }

      function renderQuestion() {
        type = questions[pos][0];
        console.log("type is: " + type);
        if (type == "mc") {
          console.log("mc case");
          renderMCQuestion();
        } else if (type == "diagram") {
          console.log("diagram case");
          renderDiagram();
        }
      }
      renderQuestion();

      //create a multiple-choice function
      function renderMCQuestion() {
        test = _("test");
        if (pos >= questions.length) {
          test.innerHTML = "<h2>You got "+correct+" of "+questions.length+" questions correct</h2>";
          _("test_status").innerHTML = "Homework Completed";
          pos = 0;
          correct = 0;
          return false;
        }
        _("test_status").innerHTML = "Question " + (pos+1) + " of " + questions.length;
        question = questions[pos][1];
        chA = questions[pos][2];
        chB = questions[pos][3];
        chC = questions[pos][4];
        chD = questions[pos][5];
        test.innerHTML = "<h3>"+question+"</h3>";
        test.innerHTML += "<input type='radio' name = 'choices' value='A'> "+chA+"<br>";
        test.innerHTML += "<input type='radio' name = 'choices' value='B'> "+chB+"<br>";
        test.innerHTML += "<input type='radio' name = 'choices' value='C'> "+chC+"<br>";
        test.innerHTML += "<input type='radio' name = 'choices' value='D'> "+chD+"<br><br>";
        test.innerHTML += "<button onclick='checkAnswer()'>Submit</button>";
        console.log("current question = " + (pos+1));
      }

      //create a diagram question
      function renderDiagram() {
        console.log("we're in renderDiagram");
        _("test_status").innerHTML = "Question " + (pos+1) + " of " + questions.length;
        question = questions[pos][1];
        test.innerHTML = "<h3>"+question+"</h3>";
        test.innerHTML += "<img id = 'q_diagram' src =" + questions[pos][2] + " alt='Sorry, could not load image.' usemap = 'ponytail' usemap = 'frontalCortex' ></img>";
        test.innerHTML += "<map name = 'ponytail'> <area shape='poly' coords='427,33, 456,12, 506,5, 573,38, 578,219, 576,330, 599,377, 618,517, 598,560, 539,446, 459,371, 467,290, 463,104, 423,26' alt='ponytail face' href='javascript: alert(\"Yes you can!\")'> <area shape = 'poly' coords='167,232, 116,193, 113,135, 162,84, 231,65, 324,74, 267,182' href='javascript: alert(\"No, idiot that is the frontal cortex!\")'> ";
      }

      function checkAnswer(){
        choices = document.getElementsByName("choices");//get all the choices
        for (var i=0; i<choices.length; i++) { //traverse through the choices
          if (choices[i].checked) {            //check which choice the student chose
            choice = choices[i].value;         //set student's choice to var choice
            if (choice == questions[pos][6]) { //check if student's choice is correct
              alert("Correct");
              correct++;  
              tries = 0;
              pos++;      
            } else if (choice != questions[pos][6] && tries < 1) {
              tries++;
              console.log("tries = " + tries);
              alert("Try again");
              //need to somehow display the same question again 
            } else if (choice != question[pos][6] && tries >= 1) {
              tries = 0;
              pos++;
              alert("Incorrect");
            }
            renderQuestion();
          }
        }
      }
      window.addEventListener("load", renderMCQuestion, false);
    </script>
2

There are 2 answers

0
rpadovani On

The easiest solution to your question is to move the pos++ instruction in the first branch of the if, so it will be executed only when the user chooses the right solution, like this:

if (choices[i].checked) {            //check which choice the student chose
  choice = choices[i].value;         //set student's choice to var choice
  if (choice == questions[pos][5]) { //check if student's choice is correct
    alert("Correct");
    correct++;  
    tries = 0;
    pos++;
  } else if (choice != questions[pos][5] && tries < 1) {
    tries++;
    console.log("tries = " + tries);
    alert("Try again");
    //need to somehow display the same question again
  } else if (choice != question[pos][5] && tries >= 1) {
    alert("Incorrect");
  }
  renderQuestion();      
}

In your example you set tries = 0 and pos++ without checking if the answer was the right one, so it always goes to the next question

0
Jon Hartmann On

If renderQuestion() always renders the next question, then yeah, you always call it at the end, regardless of the user's answer.

So, in your loop, you have an if-else-if-else-if-else block... why not just keep an array of "attempts[pos] = (attempts[pos] || 0)" then attempts[pos]++. Then you can check if they are right or wrong (one if-else) and then lastly check if attempts[pos] > 1 (2 guesses) to navigate?

var answers = {};

function renderNextQuestion () {
    pos++;
    renderQuestion();
}

for (var i=0; i<choices.length; i++) { //traverse through the choices
    if (choices[i].checked) {            //check which choice the student chose
        answers[pos] = (answers[pos] || { attempts: 0, correct: false });
        answers[pos].attempts++;

        // Did they get it right?
        answers[pos].correct = (choices[i].value == questions[pos][5]);

        // Correct answer
        if (answers[pos].correct) {
            alert("Correct");
            renderNextQuestion()

        // Incorrect, but first guess
        } else if (answers[pos].attempts < 2) {
            alert("Try again");
        // Incorrect and not the first
        } else {
            alert("Incorrect");
            renderNextQuestion();
        }
    }
}

I realize that that does get rid of the "correct" counter, but you can easily get that back by just writing a function to run on the answers object to get back the number of answers with the correct flag on.