Validate an ASP.NET CheckBoxList with JavaScript or jQuery (at least one, at most N)
A RequiredFieldValidator cannot target a CheckBoxList: the list renders many inputs, not one value. The standard answer is a CustomValidator with a client-side function and the same rule on the server. The client function is a few lines in plain JavaScript or jQuery.
Markup
<asp:CheckBoxList ID="cblTopics" runat="server" RepeatLayout="Flow" CssClass="topics">
<asp:ListItem Value="web">Web Forms</asp:ListItem>
<asp:ListItem Value="sql">SQL Server</asp:ListItem>
<asp:ListItem Value="js">JavaScript</asp:ListItem>
<asp:ListItem Value="css">CSS</asp:ListItem>
</asp:CheckBoxList>
<asp:CustomValidator ID="cvTopics" runat="server" Display="Dynamic"
ErrorMessage="Choose between one and three topics."
ClientValidationFunction="validateTopics"
OnServerValidate="cvTopics_ServerValidate" />
<asp:Button ID="btnSubscribe" runat="server" Text="Subscribe" OnClick="btnSubscribe_Click" />
Leave ControlToValidate empty: a CustomValidator without it still runs, and it would not accept a CheckBoxList as target anyway. The list renders one <input type="checkbox"> per item, with ids such as cblTopics_0, cblTopics_1, inside a container whose id is the list's ClientID.
Client function: plain JavaScript
<script>
function validateTopics(source, args) {
var list = document.getElementById("<%= cblTopics.ClientID %>");
var checked = list.querySelectorAll("input[type=checkbox]:checked").length;
args.IsValid = checked >= 1 && checked <= 3;
}
</script>
ASP.NET calls the function with the validator (source) and an object whose IsValid you set. The check runs when the button is clicked, before the postback.
Client function: jQuery
function validateTopics(source, args) {
var checked = $("#<%= cblTopics.ClientID %> input:checkbox:checked").length;
args.IsValid = checked >= 1 && checked <= 3;
}
// Optional: re-validate as soon as a box changes, so the message disappears immediately
$(function () {
$("#<%= cblTopics.ClientID %>").on("change", "input", function () {
ValidatorValidate(document.getElementById("<%= cvTopics.ClientID %>"));
});
});
ValidatorValidate is part of ASP.NET's validation script and re-runs a single validator.
Server-side check (always)
protected void cvTopics_ServerValidate(object source, ServerValidateEventArgs args)
{
int count = cblTopics.Items.Cast<ListItem>().Count(i => i.Selected);
args.IsValid = count >= 1 && count <= 3;
}
protected void btnSubscribe_Click(object sender, EventArgs e)
{
if (!IsValid) return; // validators have run; never skip this
var topics = cblTopics.Items.Cast<ListItem>().Where(i => i.Selected).Select(i => i.Value).ToList();
Subscribe(topics);
}
Client validation is for convenience; a user can disable it or post directly. Page.IsValid covers both the server-side handler and the other validators on the page.
Accessibility
With RepeatLayout="Flow" the items are rendered in spans; the default Table layout uses a table. In both cases, wrap the list in a <fieldset> with a <legend> ("Topics") so screen readers announce the group name with each box.
Same technique for other list controls: RadioButtonList and DropDownList.
Common mistakes
- Setting
ControlToValidateto the CheckBoxList: ASP.NET throws, because the control cannot be validated directly. - Counting with a page-wide selector such as
$("input:checked"): other check boxes on the page are counted too. Scope the search to the list. - Forgetting the server function: with script disabled, the client function never runs and the rule is not enforced.
Official documentation
- Microsoft Learn: CustomValidator, CustomValidator.ClientValidationFunction, CheckBoxList
- jQuery API: :checked selector