Lelio Faieta
Lelio Faieta

Reputation: 6663

html5 validation on checkboxes

HTML5 form validation will not cover the situation where, starting from a group of checkboxes, at least one of them is checked. If a checkbox has the required attribute it must be checked, this is what I can get at most. So I built a workaround that works fine (code in the snippet). The issue is that this works for one group of checkboxes. But I want my code to be valid even if I add more chackboxes groups. I need a suggestion on how to make it valid for multiple groups. Any idea?

function bindItemsInput() {
    var inputs = document.querySelectorAll('[name="option[]"]')
    var radioForCheckboxes = document.getElementById('radio-for-checkboxes')
    function checkCheckboxes () {
        var isAtLeastOneServiceSelected = false;
        for(var i = inputs.length-1; i >= 0; --i) {
            if (inputs[i].checked) isAtLeastOneCheckboxSelected = true;
        }
        radioForCheckboxes.checked = isAtLeastOneCheckboxSelected
    }
    for(var i = inputs.length-1; i >= 0; --i) {
        inputs[i].addEventListener('change', checkCheckboxes)
    }
}
bindItemsInput() // call in window onload
.checkboxs-wrapper {
  position: relative;
}
.checkboxs-wrapper input[name="radio-for-required-checkboxes"] {
    position: absolute;
    margin: 0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    -webkit-appearance: none;
    pointer-events: none;
    border: none;
    background: none;
}
<form>
  <div class="checkboxs-wrapper">
    <input id="radio-for-checkboxes" type="radio" name="radio-for-required-checkboxes" required/>
    <input type="checkbox" name="option[]" value="option1"/>
    <input type="checkbox" name="option[]" value="option2"/>
    <input type="checkbox" name="option[]" value="option3"/>
  </div>
  <input type="submit" value="submit"/>
</form>

A second snippet with the relevant HTML (not working, goal of the question is to fix this). It will have now the same ID for the radio button: that is invalid and is the reason of the question:

function bindItemsInput() {
    var inputs = document.querySelectorAll('[name="option[]"]')
    var radioForCheckboxes = document.getElementById('radio-for-checkboxes')
    function checkCheckboxes () {
        var isAtLeastOneServiceSelected = false;
        for(var i = inputs.length-1; i >= 0; --i) {
            if (inputs[i].checked) isAtLeastOneCheckboxSelected = true;
        }
        radioForCheckboxes.checked = isAtLeastOneCheckboxSelected
    }
    for(var i = inputs.length-1; i >= 0; --i) {
        inputs[i].addEventListener('change', checkCheckboxes)
    }
}
bindItemsInput() // call in window onload
.checkboxs-wrapper {
  position: relative;
}
.checkboxs-wrapper input[name="radio-for-required-checkboxes"] {
    position: absolute;
    margin: 0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    -webkit-appearance: none;
    pointer-events: none;
    border: none;
    background: none;
}
<form>
  <div class="checkboxs-wrapper">
    <input id="radio-for-checkboxes" type="radio" name="radio-for-required-checkboxes" required/>
    <input type="checkbox" name="option[]" value="option1"/>
    <input type="checkbox" name="option[]" value="option2"/>
    <input type="checkbox" name="option[]" value="option3"/>
  </div>
  <div class="checkboxs-wrapper">
    <input id="radio-for-checkboxes" type="radio" name="radio-for-required-checkboxes" required/>
    <input type="checkbox" name="products[]" value="option1"/>
    <input type="checkbox" name="products[]" value="option2"/>
    <input type="checkbox" name="products[]" value="option3"/>
  </div>
  <input type="submit" value="submit"/>
</form>

The form will be valid if at least on products[] checkbox and one option[] checkbox is checked. So I need the javascript to run indipendently for option[] and for products[]. If I have selected one item in groups[] but none in products[] then only products will be surrounded by the box and marked for completition

Upvotes: 0

Views: 181

Answers (3)

Daniel Manta
Daniel Manta

Reputation: 6683

Using jQuery this can be done with a lot less code.

$(document).ready(function() {
  var checkCheckboxesInSameGroup = function() {
    var inputs = $(this).children("input[name='option[]']");
    var radio = $(this).children("input[name^='radio-for-group']")[0];
    radio.checked = inputs.is(":checked");
  };
  $(".checkboxs-wrapper").on('change', checkCheckboxesInSameGroup);
});
.checkboxs-wrapper {
  position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
  <div class="checkboxs-wrapper">
    <input type="radio" name="radio-for-group1" required/>
    <input type="checkbox" name="option[]" value="option1"/>
    <input type="checkbox" name="option[]" value="option2"/>
    <input type="checkbox" name="option[]" value="option3"/>
  </div>
  <div class="checkboxs-wrapper">
    <input type="radio" name="radio-for-group2" required/>
    <input type="checkbox" name="option[]" value="option1"/>
    <input type="checkbox" name="option[]" value="option2"/>
    <input type="checkbox" name="option[]" value="option3"/>
  </div>   
</form>

Upvotes: 0

Mister Jojo
Mister Jojo

Reputation: 22265

So this what I imagine you are looking for:

const myForm = document.forms['my-form']

myForm.addEventListener('change', bindItemsInput)  // global change event listener 

function bindItemsInput(e) // 
  {
  if (!e.target.matches('div.checkboxs-wrapper input[type=checkbox]'))  return  
    // to reject unconcerned checkbox 

  let groupDiv   = e.target.closest('div.checkboxs-wrapper')
    , radioGroup = groupDiv.querySelector('input[type=radio]')
    , checkGroup = groupDiv.querySelectorAll('input[type=checkbox]')
    ;
  radioGroup.checked = [...checkGroup].reduce((flag,chkBx)=>flag || chkBx.checked, false)
  }


// ------ verification part-------------------
myForm.onsubmit=e=> // to verify
  {
  e.preventDefault() // disable submit for testing
  console.clear()

  // chexboxes checked values:
  let options  = [...myForm['option[]'] ].reduce((r,s)=>{ if (s.checked) r.push(s.value);return r},[])
    , products = [...myForm['product[]'] ].reduce((r,s)=>{ if (s.checked) r.push(s.value);return r},[])

  console.log('options = ', JSON.stringify( options ))
  console.log('products = ', JSON.stringify( products ))

  myForm.reset()   // clear anything for new testing
  console.log(' form reseted')
  }
  <form name="my-form">

    <div class="checkboxs-wrapper">
      <input type="radio" name="rGroup_1" required >
      <input type="checkbox" name="option[]" value="option1">
      <input type="checkbox" name="option[]" value="option2">
      <input type="checkbox" name="option[]" value="option3">
    </div>

    <div class="checkboxs-wrapper">
      <input type="radio" name="rGroup_2" required> 
      <input type="checkbox" name="product[]" value="product1">
      <input type="checkbox" name="product[]" value="product2">
      <input type="checkbox" name="product[]" value="product3">
    </div>
    
    <button type="submit">submit</button>
  </form>

Upvotes: 1

Frenchy
Frenchy

Reputation: 16997

if i understant. so you have to give another id and another name of course, try this:

function bindItemsInput() {
    var inputs = $("input[type=checkbox]");
    var radios = $("input[type=radio]");
    function checkCheckboxes () {
        var isAtLeastOneServiceSelected = false;
        for(var i = inputs.length-1; i >= 0; --i) {
            if (inputs[i].checked) isAtLeastOneCheckboxSelected = true;
        }
        radios.each( function(){
            $(this).checked = $(this).siblings($("input[type=checkbox]:checked")).length > 0;
        });
    }
    for(var i = inputs.length-1; i >= 0; --i) {
        inputs[i].addEventListener('change', checkCheckboxes)
    }
}

Upvotes: 0

Related Questions