34

I have a list of checkboxes with the same name attribute, and I need to validate that at least one of them has been selected.

But when I use the html5 attribute "required" on all of them, the browser (chrome & ff) doesn't allow me to submit the form unless all of them are checked.

sample code:

<label for="a-0">a-0</label>
<input type="checkbox" name="q-8" id="a-0" required />
<label for="a-1">a-1</label>
<input type="checkbox" name="q-8" id="a-1" required />
<label for="a-2">a-2</label>
<input type="checkbox" name="q-8" id="a-2" required />

When using the same with radio inputs, the form works as expected (if one of the options is selected the form validates)

According to Joe Hopfgartner (who claims to quote the html5 specs), the supposed behaviour is:

For checkboxes, the required attribute shall only be satisfied when one or more of the checkboxes with that name in that form are checked.

For radio buttons, the required attribute shall only be satisfied when exactly one of the radio buttons in that radio group is checked.

am i doing something wrong, or is this a browser bug (on both chrome & ff) ??

Community
  • 1
  • 1
anna.mi
  • 1,549
  • 1
  • 14
  • 28
  • 3
    Probably more straightforward (and backwards compatible) to use javascript & jQuery to validate your checkboxes. – Nathan May 04 '11 at 14:01
  • 2
    The problem with @Joe Hopfgartner's quote is that it comes from a currently unauthoritative source: http://www.whatwg.org/specs/web-forms/current-work/ That's the old Web Forms 2 work; the top of the doc admits that it's been superseded by HTML5. Don't base your expectations on that document! – james.garriss Oct 14 '11 at 12:17

8 Answers8

32

You can make it with jQuery a less lines:

$(function(){

    var requiredCheckboxes = $(':checkbox[required]');

    requiredCheckboxes.change(function(){

        if(requiredCheckboxes.is(':checked')) {
            requiredCheckboxes.removeAttr('required');
        }

        else {
            requiredCheckboxes.attr('required', 'required');
        }
    });

});

With $(':checkbox[required]') you select all checkboxes with the attribute required, then, with the .change method applied to this group of checkboxes, you can execute the function you want when any item of this group changes. In this case, if any of the checkboxes is checked, I remove the required attribute for all of the checkboxes that are part of the selected group.

I hope this helps.

Farewell.

vantesllar
  • 469
  • 4
  • 13
  • 1
    Awesome! I think it's good to add .trigger('change') next to .change(...) bind to ensure rule works if no input clicked. – userlond Jan 27 '16 at 07:20
  • It fails if there is no change! I've already ticked one checkbox and saved, when I try to save again, it ask me to tick another checkbox of same group! We should not use onChange! All the answers with on Change having this bug.https://imgur.com/a/jAKdv7z – Saurabh Dhar Jul 29 '18 at 23:23
  • @SaurabhDhar you have to call `requiredCheckboxes.trigger('change');` if you have a default `checked` checkbox. See my answer for an example. – Will B. Oct 20 '19 at 19:55
22

Sorry, now I've read what you expected better, so I'm updating the answer.

Based on the HTML5 Specs from W3C, nothing is wrong. I created this JSFiddle test and it's behaving correctly based on the specs (for those browsers based on the specs, like Chrome 11 and Firefox 4):

<form>
    <input type="checkbox" name="q" id="a-0" required autofocus>
    <label for="a-0">a-1</label>
    <br>

    <input type="checkbox" name="q" id="a-1" required>
    <label for="a-1">a-2</label>
    <br>

    <input type="checkbox" name="q" id="a-2" required>
    <label for="a-2">a-3</label>
    <br>

    <input type="submit">
</form>

I agree that it isn't very usable (in fact many people have complained about it in the W3C's mailing lists).

But browsers are just following the standard's recommendations, which is correct. The standard is a little misleading, but we can't do anything about it in practice. You can always use JavaScript for form validation, though, like some great jQuery validation plugin.

Another approach would be choosing a polyfill that can make (almost) all browsers interpret form validation rightly.

Hutch Moore
  • 124
  • 1
  • 12
Erick Petrucelli
  • 14,386
  • 8
  • 64
  • 84
  • Erick, exactly what statements in HTML5 make you think that the required attribute governs each individual checkbox even when they are in a group (i.e., have the same name)? I can't find that it specifies clearly one way or the other. – james.garriss Oct 14 '11 at 12:30
  • Chrome 26 and Firefox 20 are asking for all checkboxes to be checked on the linked jsFiddle http://jsfiddle.net/ErickPetru/kXsj9/6/ – John-Philip Apr 09 '13 at 09:36
  • 1
    @John-Philip, sure! Exactly like I said in my answer and like the [specs](http://dev.w3.org/html5/markup/input.checkbox.html#input.checkbox.attrs.required) says "that **the element** is a required part of form submission", the `required` attribute works for each element but not for groups of elements. Not a very usable thing, but is the way it's meant to be in the specs. – Erick Petrucelli Apr 09 '13 at 17:16
10

To provide another approach similar to the answer by @IvanCollantes. It works by additionally filtering the required checkboxes by name. I also simplified the code a bit and checks for a default checked checkbox.

jQuery(function($) {
  var requiredCheckboxes = $(':checkbox[required]');
  requiredCheckboxes.on('change', function(e) {
    var checkboxGroup = requiredCheckboxes.filter('[name="' + $(this).attr('name') + '"]');
    var isChecked = checkboxGroup.is(':checked');
    checkboxGroup.prop('required', !isChecked);
  });
  requiredCheckboxes.trigger('change');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form target="_blank">
  <p>
    At least one checkbox from each group is required...
  </p>
  <fieldset>
    <legend>Checkboxes Group test</legend>
    <label>
      <input type="checkbox" name="test[]" value="1" checked="checked" required="required">test-1
    </label>
    <label>
      <input type="checkbox" name="test[]" value="2" required="required">test-2
    </label>
    <label>
      <input type="checkbox" name="test[]" value="3" required="required">test-3
    </label>
  </fieldset>
  <br>
  <fieldset>
    <legend>Checkboxes Group test2</legend>
    <label>
      <input type="checkbox" name="test2[]" value="1" required="required">test2-1
    </label>
    <label>
      <input type="checkbox" name="test2[]" value="2" required="required">test2-2
    </label>
    <label>
      <input type="checkbox" name="test2[]" value="3" required="required">test2-3
    </label>
  </fieldset>
  <hr>
  <button type="submit" value="submit">Submit</button>
</form>
Will B.
  • 17,883
  • 4
  • 67
  • 69
  • Do you have any idea how to override the default message in popup with our own message? – Vivek Sancheti Jun 24 '16 at 09:21
  • @VivekSancheti see http://stackoverflow.com/questions/5272433/html5-form-required-attribute-set-custom-validation-message – Will B. Jun 28 '16 at 15:29
  • Thats not working. http://stackoverflow.com/q/38069634/5278270 I tried it and have posted this question. – Vivek Sancheti Jun 29 '16 at 10:35
  • How can I change the message from "Please check this box if you want to proceed" to a custom message? – FusionDesign May 25 '17 at 15:09
  • Use `setCustomValidity` https://jsfiddle.net/0bz3bkpz/. e.g. `checkboxGroup.each(function() { if (!isChecked) { this.setCustomValidity('Hello World'); } else { this.setCustomValidity(''); } });` – Will B. May 25 '17 at 15:35
7

i had the same problem, my solution was apply the required attribute to all elements

<input type="checkbox" name="checkin_days[]" required="required" value="0" /><span class="w">S</span>
<input type="checkbox" name="checkin_days[]" required="required" value="1" /><span class="w">M</span>
<input type="checkbox" name="checkin_days[]" required="required" value="2" /><span class="w">T</span>
<input type="checkbox" name="checkin_days[]" required="required" value="3" /><span class="w">W</span>
<input type="checkbox" name="checkin_days[]" required="required" value="4" /><span class="w">T</span>
<input type="checkbox" name="checkin_days[]" required="required" value="5" /><span class="w">F</span>
<input type="checkbox" name="checkin_days[]" required="required" value="6" /><span class="w">S</span>

when the user check one of the elements i remove the required attribute from all elements:

var $checkedCheckboxes = $('#recurrent_checkin :checkbox[name="checkin_days[]"]:checked'),
    $checkboxes = $('#recurrent_checkin :checkbox[name="checkin_days[]"]');

$checkboxes.click(function() {

if($checkedCheckboxes.length) {
        $checkboxes.removeAttr('required');
    } else {
        $checkboxes.attr('required', 'required');
    }

 });
Rommel Castro
  • 460
  • 4
  • 13
  • 2
    The name shouldn't include brackets, but most importantly this is a bad solution because you're having contradicting semantics vs logic. The previous answer is the more correct way but if you want a jQuery polyfill version try this SO question: http://stackoverflow.com/questions/1535187/jquery-validate-form-with-multiple-checkboxes-at-least-one-must-be-checked – John-Philip Apr 09 '13 at 09:32
7

Here is improvement for icova's answer. It also groups inputs by name.

$(function(){
  var allRequiredCheckboxes = $(':checkbox[required]');
  var checkboxNames = [];

  for (var i = 0; i < allRequiredCheckboxes.length; ++i){
    var name = allRequiredCheckboxes[i].name;
    checkboxNames.push(name);
  }

  checkboxNames = checkboxNames.reduce(function(p, c) {
    if (p.indexOf(c) < 0) p.push(c);
    return p;
  }, []);

  for (var i in checkboxNames){
    !function(){
      var name = checkboxNames[i];
      var checkboxes = $('input[name="' + name + '"]');
      checkboxes.change(function(){
        if(checkboxes.is(':checked')) {
          checkboxes.removeAttr('required');
        } else {
          checkboxes.attr('required', 'required');
        }
      });
    }();
  }

});
Zhomart
  • 702
  • 1
  • 7
  • 19
3

A little jQuery fix:

$(function(){
    var chbxs = $(':checkbox[required]');
    var namedChbxs = {};
    chbxs.each(function(){
        var name = $(this).attr('name');
        namedChbxs[name] = (namedChbxs[name] || $()).add(this);
    });
    chbxs.change(function(){
        var name = $(this).attr('name');
        var cbx = namedChbxs[name];
        if(cbx.filter(':checked').length>0){
            cbx.removeAttr('required');
        }else{
            cbx.attr('required','required');
        }
    });
});
Yukulélé
  • 15,644
  • 10
  • 70
  • 94
0

Building on icova's answer, here's the code so you can use a custom HTML5 validation message:

$(function() {
    var requiredCheckboxes = $(':checkbox[required]');
    requiredCheckboxes.change(function() {
        if (requiredCheckboxes.is(':checked')) {requiredCheckboxes.removeAttr('required');}
        else {requiredCheckboxes.attr('required', 'required');}
    });
    $("input").each(function() {
        $(this).on('invalid', function(e) {
            e.target.setCustomValidity('');
            if (!e.target.validity.valid) {
                e.target.setCustomValidity('Please, select at least one of these options');
            }
        }).on('input, click', function(e) {e.target.setCustomValidity('');});
    });
});
OMA
  • 3,442
  • 2
  • 32
  • 39
0
var verifyPaymentType = function () {
   //coloque os checkbox dentro de uma div com a class checkbox
  var inputs =  window.jQuery('.checkbox').find('input');
  var first = inputs.first()[0];

  inputs.on('change', function () {
    this.setCustomValidity('');
  });

  first.setCustomValidity( window.jQuery('.checkbox').find('input:checked').length === 0 ? 'Choose one' : '');
}

window.jQuery('#submit').click(verifyPaymentType);

}