I have read some other solutions to this, but they all suffer from a number of disadvantages, in my opinion, one or several of the following:
User agent will communicate about some particular checkbox when that checkbox or all checkboxes are marked up as required or when the checkbox is explicitly flagged as invalid with setCustomValidity
. The result is confusing to the user as the user agent does its earnest clearly communicating to the user that the user must check that particular checkbox to proceed, which isn't what you'd want anyway -- you merely want the user to check any checkbox in the group, in the very least.
Flagging checkboxes as valid/invalid when checking and unchecking these is also a bit of a UX dead end -- you will need to validate the form containing these at submission anyway, even when the user hasn't touched any of the checkboxes once prior to attempting to submit the form -- how are you validating the form? Marking checkboxes as required to ensure at least one gets checked before form may be submitted gets you the aforementioned problem, again.
In light of these drawbacks, I, for one, have used the following approach:
form.addEventListener("submit", ev => {
if(!Array.prototype.some.call(checkboxes(), checkbox => checkbox.checked)) {
tell_user_they_must_check_some_checkbox(); /// Instead of `setCustomValidity` which we can't use since we don't have a single particular checkbox to flag as invalid, just implement our own notification
ev.preventDefault(); /// Prevent form submission -- the "submit" event only fires if the form was validated successfully in the first place, so it's up to us to stop the submission (according to our own validation constraints that cannot be expressed with `required` on some form controls).
}
});
That's all I think there is to it. I left out selection of actual checkboxes since that's not the point the code is trying to illustrate, but you'll have to define the group of checkboxes you want to validate with the above by implementing checkboxes
. It can be a generator function or simply one that returns the array or some HTML collection of these.
The important thing is that setCustomValidity
can't meaningfully be used at all since there is no way to flag some group or at least contextually appropriate control of the form as invalid. If you find one, like some submit button, for example, the tell_user_they_must_check_some_checkbox
reduces to a setCustomValidity
call. You still need to prevent default because there won't be any checking of actually invalid controls after the "submit" event has been processed, that happens before, so we need to abort submission ourselves, if applicable.
As an exercise to the reader, you can implement grouping with some data-
attributes or just grouping implicitly by containing the checkboxes where you want at least one in the group selected, in a fieldset
(or even a div
or whatever) and checking against those grouped -- like I said, which checkboxes you want to select for validating against the aforementioned condition, is entirely up to the form designer. You can even experiment with identical name
for all grouped checkboxes, much like is done with radio buttons to good effect, but that may break traditional form submission (multiple checkboxes with the same name may not be serialized by the user agent into a POST request as you'd want them to be)