Checking if a set of checkboxes have a certain number of options checked I'm coding a jQuery plugin that basically checks if a set of checkboxes groups have at least x options checked. A checkbox group is named as name=group[]. You'd call the plugin like:

$(':checkbox').checkpass({min: 1}) // Make sure at least one checkbox per group is checked


I've been working on this piece of code for a couple days trying to make it as short and fast as possible because I'm going to implement this on a very large form.  Is it possible to make it shorter/faster, or have I reached the limit?

You can check a live example here.

(function($) {
    $.fn.checkpass = function(options) {

        var opt = $.extend({
            min: function() {
                return (min === 'undefined') ? 1 : min;
            },
            errorContainer: '&lt;ul class="checkpass-errors"&gt;',
            errorWrap: '&lt;li class="error"&gt;'
        }, options);

        var $ckboxes = this,
            names = [],
            num = 0,
            $errors = $(opt.errorContainer);

        $ckboxes.each(function() {
            var name = this.name;
            if (!~$.inArray(name, names)) {
                names.push(name);
            }
        });

        for (var i = 0, l = names.length; i &lt; l; i++) {
            $checked = $ckboxes.filter('[name="' + names[i] + '"]:checked');
            if ($checked[opt.min - 1]) {
                num++;
            } else {
                if (opt.min &gt; 0) {
                    var $err = $(opt.errorWrap),
                        _remaining = opt.min - $checked.length,
                        _name = names[i].match(/\w+/).toString()
                                .replace(/^\w/, function($0) {
                                    return $0.toUpperCase();
                                });
                    $err.html('&lt;strong&gt;' + _name +
                              ':&lt;/strong&gt; Check ' +
                              _remaining + ' more.').appendTo($errors);
                }
            }
        }

        return {
            isValid: !! (num === names.length),
            errors: $errors
        };
    };
})(jQuery);
Instead of using an array for names, you could use some implementation of a set, which automatically skips duplications; see here.

Instead of keeping the num variable, I would just check if errors is empty to set isValid.

Actually, you can combine both suggestions by replacing names by an object that stores the unique names associated with their respective error message (or null if it passes).

{"animals[]" : someErrorMessage,  "drinks[]" : null, "colors[]" : someOtherErrorMessage}


You would need to replace your for-loop by a for (name in names) and you would only check if there is not enough check marks and add an error if that's the case.

I played with your fiddle and made a fork:

(function($) {
    $.fn.isIncomplete = function(options) {

        var opt = $.extend({
            min: function() {
                return (min === 'undefined') ? 1 : min;
            }
        }, options);

        var $ckboxes = this;
        var names = {};

        $ckboxes.each(function() {
            names[this.name] = null;
        });

        var errors = [];
        for (name in names) {
            var $checked = $ckboxes.filter('[name="' + name + '"]:checked');
            if ($checked.length &lt; opt.min) {
                isValid = false;
                var _remaining = opt.min - $checked.length;
                var _name = name.match(/\w+/).toString().replace(/^\w/, function($0) {
                    return $0.toUpperCase();
                });
                errors.push('&lt;strong&gt;' + _name + ':&lt;/strong&gt; Check ' + _remaining + ' more.');
            }
        }

        if (errors.length == 0) return null;
        else return errors;
    };
})(jQuery);


// Testing...
$('button').click(function() {
    var incompleteErrors = $(':checkbox').isIncomplete({
        min: 1
    });
    if (incompleteErrors) {
        ul = $('&lt;ul&gt;');
        for (var i = 0; i &lt; incompleteErrors.length; i++)
        ul.append($('&lt;li&gt;').html(incompleteErrors[i]));
        $("#errors").html(ul);
    } else {
        $("#errors").html("");
        alert('Passed!');
    }
});​