Condition the user to check all radio buttons before submitting the form?

Hello! I have a form which is actually a Quiz. At the end there is a “showScore!” button. Right now, users can click it before they have answered all 10 questions and thus see partial score. I want them to be able to see it only after 10 questions, else get an alert"please complete all questions first". A pseudoCode will be:

function totalSum() {
if (ALL radio boxes are checked) {document.querySelector("#score").innerHTML = points;}
else {alert("Please fill all before asking to see your score.");}

How will you write such a code? Thx,

Better to use a client-side validation tool for this. I use jQuery ValidationEngine for this. You just need to set them up as “group required” and it would not allow the form to be submitted without all of them being selected.

Sorry for not mentioning… I need it to be in pure JS… No libraries…

What does your form mark-up look like?

How is points (the score) being made available inside the function?

It can be seen here (New to the site, hope It’s okay to link to Codepen.io):

There’s probably a better way to do this but this seems to work.

Change your totalSum to this:

function totalSum() {
    var notChecked = false;
    var radios = [];
    var inputs = niceForm.elements;

    for (var i = 0; i < inputs.length; ++i) {
        if (inputs[i].type == 'radio') {
            radios.push(inputs[i]);
        }
    }

    for (var i = 0; i < radios.length; i += 2) {
        if (!(radios[i].checked || radios[i + 1].checked)) {
            alert('Must select all items first');
            notChecked = true;
            break;
        }
    }
    if (notChecked === false) {
        document.querySelector("#sc").innerHTML = points;
    }
}

It’s probably too verbose for the experts here and I’m sure it could be refined. :smile:

You should also look at removing the inline click handlers as that is bad practice and do it all from the script. You can set the value of the input and then check that for your score (and would still work if js was disabled as the form could be submitted and parsed serverside).

Your html is also invalid as IDs must be unique and labels should point to the input correctly. You cannot nest forms either.

Here is a better layout for the html and I removed the inline handlers just to show how it can be done without cluttering up the markup.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<style>
#niceForm {
	max-width: 582px;
	margin: 0 auto;
	background:lightgray;
	padding:16px 16px 0;
}
#niceForm fieldset {
	margin:0;
	padding:0;
	border:none;
}
#niceForm legend {
	font-weight:bold;
	display:block;
	margin:0;
	padding:0;
	text-indent:0;
	font-size:120%;
}
#coolestDiv {
	margin: 1em -16px 0;
	padding: 1em;
	background: lavender;
}
.info{
	display:inline-block;
	vertical-align:baseline;
	padding:15px 10px 10px 0;
}
#niceForm label,#niceForm input{vertical-align:baseline}
</style>
</head>

<body>
<form id="niceForm" name="loveQuiz" action="/test">
  <fieldset>
    <legend>This is the quiz</legend>
    <div> <span class="info">Q1 - doYouLoveChocolate?</span>
      <input type="radio" name="g1" id="q1y" value="yes" >
      <label for="q1y">Yes</label>
      <input type="radio" name="g1" id="q1n" value="no">
      <label for="q1n">No</label>
    </div>
    <div> <span class="info">Q2 - doYouLoveLinux?</span>
      <input type="radio" name="g2"  id="q2y" value="yes" >
      <label for="q2y">Yes</label>
      <input type="radio" name="g2" id="q2n" value="no" >
      <label for="q2n">No</label>
    </div>
    <div> <span class="info">Q3 - doYouLovePies?</span>
      <input  type="radio" name="g3" id="q3y" value="yes" >
      <label for="q3y">Yes</label>
      <input  type="radio" name="g3" id="q3n" value="no" >
      <label for="q3n">No</label>
    </div>
    <div id="coolestDiv">
      <input id="showScore" type="button" name="showerOfScores" value="showScore" >
      <p>Total Score: <span id="sc"></span> </p>
    </div>
  </fieldset>
</form>
<script>
document.getElementById('showScore').onclick = totalSum;

function totalSum() {
    var inputs = document.getElementById('niceForm').getElementsByTagName('input');
    var radios = [];
    var notChecked = false;
    var points = 0;

    for (var i = 0; i < inputs.length; ++i) {
        if (inputs[i].type == 'radio') {
            radios.push(inputs[i]);
        }
    }
    for (var i = 0; i < radios.length; i += 2) {
        if (!(radios[i].checked || radios[i + 1].checked)) {
            alert('Must select all items first');
            notChecked = true;
            break;
        }
    }
    if (notChecked === false) {

        for (var i = 0; i < radios.length; i += 2) {
            if (radios[i].checked) points = points + 10;
        }
        document.querySelector("#sc").innerHTML = points;
    }
}
</script>
</body>
</html>

JS is not my strong-point but the html and css is :smile:

You would need to add extra logic to the above for the points as I have simply added up the yes values but leave that as you for an exercise.

Since there is always more than one way of doing things here is my attempt. It approaches the solution by having the “Show Score” button disabled until all the questions have been answered. Only then can it be clicked and the score calculated. It’s debatable how useful the example is as a lot of assumptions have been made about what you are trying to achive.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Quiz</title>
    <style>
      ol {
        list-style: none;
      }
    </style>
  </head>
  <body>
    <form id="quiz">
      <fieldset>
        <legend>This is the quiz</legend>
        <ol>
          <li>
            Q1 - Love Chocolate? 
            <label><input type="radio" name="a1" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a1" class="no-answer"> No</label>
          </li>
          <li>
            Q2 - Love Linux?
            <label><input type="radio" name="a2" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a2" class="no-answer"> No</label>
          </li>
          <li>
            Q3 - Love Pies? 
            <label><input type="radio" name="a3" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a3" class="no-answer"> No</label>
          </li>      
        </ol>
      </fieldset>    
      <fieldset>
        <button name="showScore">Show Score</button>
        <p>Total Score: <span id="score"></span></p>
      </fieldset>
    </form>
    <script>
      (function(){
        var form = document.forms.quiz,
            showScore = form.showScore,
            score = form.querySelector('#score');
            
        /**
         * Assume two radio field answers for each question.
         * Checking that all questions have been answered can be done by seeing how many
         * radio fields have been checked and comparing the result to the number of fields divided by 2.
         */
        function allAnswered() {
          return form.querySelectorAll('input[type="radio"]:checked').length === form.querySelectorAll('input[type="radio"]').length / 2;
        }
        
        /**
         * Assuming that a 'Yes' answer is worth 10 points and a 'No' answer is zero.
         * Then the score is simply the number of checked 'Yes' answers multipled by 10.
         */
        function calcScore() {
          return form.querySelectorAll('input.yes-answer[type="radio"]:checked').length * 10;
        }
        
        /**
         * Assign the click event listener to the form.
         * If the event was triggered by answering a question then enable the button if all questions have been answered.
         */
        form.addEventListener('click', function (e) {
          if (e.target.type && e.target.type === 'radio') {
            showScore.disabled = !allAnswered();
          }
        });
        
        showScore.addEventListener('click', function (e) {
          e.preventDefault();
          score.innerHTML = calcScore();
        });
        
        /**
         * Set the inital disabled state of the button 
         * as the form may have remembered some of the field's checked states.
         */
        showScore.disabled = !allAnswered();
      })();        
    </script>
  </body>
</html>

Just to show an alternative solution the example below updates and displays the score as the questions are answered.

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Quiz</title>
    <style>
      ol {
        list-style: none;
      }
    </style>
  </head>
  <body>
    <form id="quiz">
      <fieldset>
        <legend>This is the quiz</legend>
        <ol>
          <li>
            Q1 - Love Chocolate? 
            <label><input type="radio" name="a1" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a1" class="no-answer"> No</label>
          </li>
          <li>
            Q2 - Love Linux?
            <label><input type="radio" name="a2" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a2" class="no-answer"> No</label>
          </li>
          <li>
            Q3 - Love Pies? 
            <label><input type="radio" name="a3" class="yes-answer"> Yes</label>
            <label><input type="radio" name="a3" class="no-answer"> No</label>
          </li>      
        </ol>
      </fieldset>    
      <fieldset>
        <p>Total Score: <span id="score"></span></p>
      </fieldset>
    </form>
    <script>
      (function(){
        var form = document.forms.quiz,
            score = form.querySelector('#score');

        function calcScore() {
          return form.querySelectorAll('input.yes-answer[type="radio"]:checked').length * 10;
        }

        form.addEventListener('click', function (e) {
          if (e.target.type && e.target.type === 'radio') {
            score.innerHTML = calcScore();
          }
        });
        
        score.innerHTML = calcScore();
        
      })();        
    </script>
  </body>
</html>

Nicely done :slight_smile:

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.