A Javascript quiz with results after each question?

Hi, I’m wondering if there’s a simple Javascript quiz that has, for example, 10 multiple choice questions, but on clicking the answer (from a dropdown menu) to each question, you would instantly see the result for that specific question, for example a (correct) tick, or (incorrect) x.
The idea being that you wouldn’t need to click a submit button to see the result.
Thanks.

There’s probably a way to do this with html/css where clicking on a choice will simply show/hide an X or tick mark.

Hi @dubman, are you asking for a tutorial or some sort of quiz generator? As @WebSteve, said, for the basics you don’t even need JS… for example:

.answer > :checked + label::after {
    content: '\274C';
    margin-left: 1em;
}

.answer > [data-correct]:checked + label::after {
    content: '\2705';
}
<section class="question">
  John, Paul, George and...

  <div class="answer">
    <input type="radio" name="answer-1" id="answer-1-1" data-correct="true">
    <label for="answer-1-1">Ringo</label>
  </div>

  <div class="answer">
    <input type="radio" name="answer-1" id="answer-1-2">
    <label for="answer-1-2">Django</label>
  </div>

  <div class="answer">
    <input type="radio" name="answer-1" id="answer-1-3">
    <label for="answer-1-3">Rhino</label>
  </div>
</section>

<section class="question">
  Joey, Johnny, Dee Dee and...

  <div class="answer">
    <input type="radio" name="answer-2" id="answer-2-1">
    <label for="answer-2-1">Timmy</label>
  </div>

  <div class="answer">
    <input type="radio" name="answer-2" id="answer-2-2" data-correct="true">
    <label for="answer-2-2">Tommy</label>
  </div>

  <div class="answer">
    <input type="radio" name="answer-2" id="answer-2-3">
    <label for="answer-2-3">Jimmy</label>
  </div>
</section>

For more fine-grained control – e.g. disabling the radios for a checked answer – the essential JS would be to query for the answers, and add change event listeners like so:

const questions = document.querySelectorAll('.question')

function handleChange (event) {
  // Update the UI here, such as disabling answers or
  // displaying the correct answer in a fancier manner
  const answers = event.currentTarget.querySelectorAll('input')

  answers.forEach(answer => {
    answer.disabled = true
  })
}

questions.forEach(question => {
  question.addEventListener('change', handleChange)
})
4 Likes

Out of curiousity, are you doing this for a homework assignment?

Cause… your description sounds awfully similar to several posts that happen roughly twice a year…

1 Like

How would I implement the above piece of code. Is it php? Or does it go in the page Head?
Just wanted to improve a page so that a user could see the result of each answer individually
after clicking an option in the dropdown menu. My current solution of a submit button after each question is obviously not an ideal solution. I don’t necessarily need to set it up as a typical quiz, with a global result like “7 answers correct out of 10”

This is the page in question:
http://www.profesornativo.com/English_expressions.htm

Thanks

It’s CSS. It will go in your CSS file.

1 Like

Ah so you already have the JS to check the answers:

f.onsubmit = function(){
  checkAnswers({
    question1: {answer: "c"},
    question2: {answer: "c"},
    question3: {answer: "b"},
    question4: {answer: "c"},
    question5: {answer: "a"},
    question6: {answer: "b"},
    question7: {answer: "a"},
    question8: {answer: "b"},
    question9: {answer: "c"},
    question10: {answer: "a"}
  })
  return false;
}

You can just replace onsubmit with onchange then to get an immediate feedback – each time a change event occurs inside the form, it will propagate up to the form itself and your listener function will get called. This is also known as event bubbling:

2 Likes

Hi,

As m3g4p0p said, just swap out the submit listener for a change listener and you should be good.

However, I was a little surprised to look at the code and see that I wrote (at least) part of it.

It was probably fine back then, but has since seen a couple of iterations and could be improved a little.

The first thing is, don’t use tables for layouts. You have a list of questions, so it would make sense semantically to use an ordered list.

The JS logic could also be improved. Here’s a more up-to-date version with two questions (for demo purposes):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <style>
      .result {
        display: inline-block;
        height: 20px;
        width: 20px;
      }
      .incorrect {
        background: url(http://www.profesornativo.com/x.jpg);
        background-repeat: no-repeat;
      }
      .correct {
        background: url(http://www.profesornativo.com/tick.jpg);
        background-repeat: no-repeat;
      }
    </style>
  </head>
  <body>
    <ol>
      <li>
        <p>Spanish: Me lo contó un pajarito</p>
        <p>
          A little
          <select name="question1">
            <option value="na"></option>
            <option value="a">birdie mentioned</option>
            <option value="b">bird said to</option>
            <option value="c">bird told</option>
          </select>
          me
          <span class="result"></span>
        </p>
      </li>
      <li>
        <p>Spanish: A la más minima </p>
        <p>
          At the
          <select name="question2">
            <option value="na"></option>
            <option value="a">fall of a leaf</option>
            <option value="b">sound of a voice</option>
            <option value="c">drop of a hat</option>
          </select>
          <span class="result"></span>
        </p>
      </li>
    </ol>

    <script>
      const answers = {
        question1: 'c',
        question2: 'c',
      }

      function checkAnswer(select){
        const correctAnswer = answers[select.name];
        const selectedAnswer = select.options[select.selectedIndex].value;
        const result = select.parentElement.querySelector('.result');
        result.className = 'result';

        if (selectedAnswer === correctAnswer) {
          result.classList.add('correct');
        } else {
          result.classList.add('incorrect');
        }
      }

      const selects = document.querySelectorAll('select');

      [...selects].forEach((el) => {
        el.addEventListener('change', () => {
          checkAnswer(el);
        });
      });
    </script>
  </body>
</html>
1 Like

Thanks James, your fully entitled to the credit for the code in question, and I see your current suggestion is exactly what I wanted!

And thanks to m3g4p0p, WebSteve and Gandalf - I will have a look at the suggestions!