Search code examples
jqueryvalidationrequiredfieldvalidatorjquery-form-validator

How to validate checkboxes with same css class name


I have a form that contains several questions. Some of these questions are checkbox inputs.

When a form question is generated, it follows the following structure:

  1. Contains the parent div <div class="checkbox">
  2. If the checkbox is a required field (user must select at least one checkbox for that question), the parent div gets the CSS class 'required' added to the existing class.
  3. When "Save Changes" button is clicked, it should search for all divs as following: <div class="checkbox required">
  4. If that div contains at least one checkbox that is checked, it should continue to the next, and so on, finally submitting the form if no errors found.
  5. If it comes across a <div class="checkbox required"> that does NOT have any checkboxes checked, it should print an error message next to that question.

Below is the code that I have. Both of the questions are required. It's fairly close, I just can't seem to get it quite right. It seems to treat all divs with "required" class as one object, rather than as each as their own individual object in the boxes.each section. Does anyone have any suggestions? Thank you in advance.

$(document).ready(function() {
    $("button").click(function(e){
        e.preventDefault();
        var boxes = $('div.checkbox.required');

        boxes.each(function(){
            if($(this).is(":checked"))
            {
                $(".error", this).hide();
                $("form").submit();
                return true;
            } else if($(this).not(":checked")) {
                $(".error", this).show().html("Please fill out all required fields");
                return false;
            }
        });
    });
    
});
label { font-weight: bold; }
.form-horizontal label span.error { color: red; }
.form-horizontal label div.error { color: red; }
.error {
    padding: 2px;
    font-size: 15px;
    background: #FFDFDF;
    border: 1px solid #FFCACA;
    border-radius: 5px;
    font-weight: normal;
    text-align: center;
    display: none;
}
<div class="checkbox required">
<h3>Question 1</H3>
  <div class="col-md-4">
    <span class="error"></span>
    <label id="8522[]">
      <input type="checkbox" class="" id="input-8522" name="8522[]" value="23606">
      Behavioral Follow-up
    </label><br>
    <label id="8522[]">
      <input type="checkbox" class="" id="input-8522" name="8522[]" value="23607">
      Medical Follow-up
    </label><br>
  </div>
</div>
<p><br></p>
<div class="checkbox required">
<h3>Question 2</H3>
  <div class="col-md-4">
    <span class="error"></span>
    <label id="8524[]">
      <input type="checkbox" class="" id="input-8524" name="8524[]" value="23608">
      Mild
    </label><br>
    <label id="8524[]">
      <input type="checkbox" class="" id="input-8524" name="8524[]" value="23609" checked="checked">
      Moderate
    </label><br>
    <label id="8524[]">
      <input type="checkbox" class="" id="input-8524" name="8524[]" value="23610">
      Severe
    </label><br>
  </div>
</div>

<p><br></p>

<button type="button" class="btn btn-primary action-save">
<i class="fa fa-save"></i> Save changes
</button>

<script src="https://code.jquery.com/jquery-3.6.3.slim.min.js"></script>


Solution

  • There are a couple of issues I see.

    The first is with the line if($(this).is(":checked")). $(this) in this block is referring to a div.checkbox.required since we are iterating through the list of elements that match this selector. <div> elements do not get the :checked property, so this will always evaluate to false. Instead, we want to determine if the div.checkbox.required contains any :checked inputs. We could do that with:

    const hasAnyChecked = $(this).find('input:checked').length > 0;
    

    The next issue is that the $("form").submit() is within the boxes.each loop. This will not work because if, for example, the first div.checkbox.required contains a :checked input and is therefore valid, the form will be submitted before checking the remaining div.checkbox.required elements to determine if they too are valid. Instead, we must use a variable to track that all div.checkbox.required are in a valid state and then conditionally submit the form once the boxes.each loop has completed.

    The code becomes:

    $(function() {
      $('button').on('click', function () {
        let isValid = true;
    
        $('div.checkbox.required').each(function () {
          const $this = $(this);
          const hasAnyChecked = $this.find('input:checked').length > 0;
    
          isValid = isValid && hasAnyChecked;
    
          if (hasAnyChecked) {
            $this.find('.error').hide();
          } else {
            $this.find('.error').text('Please fill out all required fields').show();
          }
        });
    
        if (isValid) {
          $("form").submit();
        }
      });
    });
    label {
      font-weight: bold;
    }
    
    .form-horizontal label span.error {
      color: red;
    }
    
    .form-horizontal label div.error {
      color: red;
    }
    
    .error {
      padding: 2px;
      font-size: 15px;
      background: #FFDFDF;
      border: 1px solid #FFCACA;
      border-radius: 5px;
      font-weight: normal;
      text-align: center;
      display: none;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="checkbox required">
      <h3>Question 1</H3>
      <div class="col-md-4">
        <span class="error"></span>
        <label id="8522[]">
          <input type="checkbox" class="" id="input-8522" name="8522[]" value="23606">
          Behavioral Follow-up
        </label><br>
        <label id="8522[]">
          <input type="checkbox" class="" id="input-8522" name="8522[]" value="23607">
          Medical Follow-up
        </label><br>
      </div>
    </div>
    <p><br></p>
    <div class="checkbox required">
      <h3>Question 2</H3>
      <div class="col-md-4">
        <span class="error"></span>
        <label id="8524[]">
          <input type="checkbox" class="" id="input-8524" name="8524[]" value="23608">
          Mild
        </label><br>
        <label id="8524[]">
          <input type="checkbox" class="" id="input-8524" name="8524[]" value="23609" checked="checked">
          Moderate
        </label><br>
        <label id="8524[]">
          <input type="checkbox" class="" id="input-8524" name="8524[]" value="23610">
          Severe
        </label><br>
      </div>
    </div>
    
    <p><br></p>
    
    <button type="button" class="btn btn-primary action-save">
      <i class="fa fa-save"></i> Save changes
    </button>