jQuery 对具有多个输入的 .form-group 进行验证

jQuery Validation on a .form-group with multiple inputs

使用 jQuery Validation 插件,我为 Bootstrap form 定义了以下内容:

$(".form-with-validation").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(element).closest('.form-group').addClass('has-error');
    },
    unhighlight: function(element) {
        $(element).closest('.form-group').removeClass('has-error');
    }
});

它适用于简单的表单。但是,当 .form-group 包含需要验证的多个输入(内联)时,带有 highlightunhighlight 的部分不起作用:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-3">
        <select class="form-control required" name="state">
            ...
        </select>
    </div>
    <div class="col-md-3">
        <input type="text" class="form-control required" name="zip">
    </div>
</div>

问题是一旦你select一个状态,输入就变得有效,它的.form-group失去.has-errorclass ,即使 同级输入(即 zip)仍然无效(即下面有一个 .help-block 跨度):

因此,我将 unhighlight 部分更改为以下内容:

unhighlight: function(element) {
    var formGroup = $(element).closest('.form-group');
    var formGroupIsValid = true;
    formGroup.find('input').each(function(){
        if (! $(this).valid())
            formGroupIsValid = false;
    });
    if (formGroupIsValid)
        formGroup.removeClass('has-error');
}

但我收到以下错误:

Uncaught RangeError: Maximum call stack size exceeded

知道为什么吗?我尝试了很多方法,但每次都得到相同的错误。

编辑

如果可能(因为样式),我更愿意坚持使用 div.form-group .has-error class。

编辑 2

Jsfiddle 演示问题。

您正在调用一个函数,该函数又调用另一个函数,依此类推,直到达到调用堆栈限制。当您调用 .valid() 时,我假设问题出在您的 .each 循环中。

不过,您不必执行任何这些操作。而不是针对 form-group,您应该专门针对输入周围的内容,这样您就不必更改 unhightlight 函数。例如:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-3 inputToValidate">
        <select class="form-control required" name="state">
            ...
        </select>
    </div>
    <div class="col-md-3 inputToValidate">
        <input type="text" class="form-control required" name="zip">
    </div>
</div>

然后将 JavaScript 代码更新为:

$(".form-with-validation").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(element).closest('.inputToValidate').addClass('has-error');
    },
    unhighlight: function(element) {
        $(element).closest('.inputToValidate').removeClass('has-error');
    }
});

I'd prefer to stick with div.form-group having .has-error class if possible

插件提供的选项无法实现。 valid/invalid classes 在被验证的元素上切换。可以修改 highlightunhighlight 函数以使用 jQuery DOM 遍历等在其他元素上切换 classes

但是,您需要在其任何子容器无效时创建父容器 "invalid" 的逻辑...该插件不具备此功能。一旦无效的子元素在父元素上触发错误 class,任何有效的子元素都会将有效的 class 应用于同一父元素。

解决方法是 外部 keyupchange 处理程序,它查看所有同级输入元素上的 classes 并切换它的父 class 相应地。基于您自己的代码且未经测试...

$('input, select').on('keyup change', function() {
    var formGroup = $(this).closest('.form-group');
    var formGroupIsValid = true;
    formGroup.find('input, select').each(function(){
        if (! $(this).valid()) {
            formGroupIsValid = false;
        }
    });
    if (formGroupIsValid) {
        formGroup.removeClass('has-error');
    } else {
        formGroup.addClass('has-error');
    }
});

I get the following error: Uncaught RangeError: Maximum call stack size exceeded .... Any ideas why?

是的,您正在从 .validate() 方法中调用 .valid() 方法(通过 unhighlight)。所以从这个方法中调用 $(this).valid() 只会导致 unhighlight 被再次调用......等等无限期。

您不必使用 form-with-validation class 您可以通过选择 inputToValidate class 来定位您想要验证的所有表单域。它也简单得多。你也可能想使用 toggleClass.

$(".inputToValidaten").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(element) {
        $(this).toggleClass('has-error');
    },
    unhighlight: function(element) {
        $(this).toggleClass('has-error');
    }
});

更新和编辑

好的。所以这里的代码可以在任何带有表单控件 class 的嵌套表单字段上工作。它将上升并使用表单组 class 获取最接近的元素,然后将验证代码添加到其中。

$(".form-control").validate({
    errorClass: "help-block",
    errorElement: "span",
    highlight: function(e) {
        $(e.target).closest('.form-group').toggleClass('has-error');
    },
    unhighlight: function(e) {
        $(e.target).closest('.form-group').toggleClass('has-error');
    }
}); 

这是我最终得到的解决方案。这比我想象的要简单。正如人们之前指出的那样,任何 form-group 应该一次只包含一个 form-control。因此,最简单的解决方案是将第二个 form-group 放在第一个里面,然后将第二个 form-control 放在那里:

<div class="form-group">
    <label class="col-md-4 control-label">State &amp; Zip</label>
    <div class="col-md-6">
        <div class="row">
            <div class="col-sm-6">
                <select class="form-control required" name="state">
                    ...
                </select>
            </div>
            <div class="col-sm-6 form-group" style="margin-bottom:0;padding-right:0">
                <input type="text" class="form-control required" name="zip">
            </div>
        </div>
    </div>
</div>

仅需几个 CSS 样式,它就可以完美运行并且看起来不错。这是 jsfiddle.