98

我正在使用出色的 jQuery Validate Plugin来验证某些表单。在一个表单上,我需要确保用户至少填写一组字段中的一个。我想我有一个很好的解决方案,并想分享它。 请提出您能想到的任何改进。

找不到内置的方法,我搜索并找到了Rebecca Murphey 的自定义验证方法,这非常有帮助。

我从三个方面改进了这一点:

  1. 让您传入一组字段的选择器
  2. 让您指定必须填充多少组才能通过验证
  3. 一旦其中一个通过验证,就将组中的所有输入显示为通过验证。(见最后对尼克·克拉弗的喊叫。)

因此,您可以说“必须填充与选择器 Y 匹配的至少 X 个输入”。

最终结果,标记如下:

<input class="productinfo" name="partnumber">
<input class="productinfo" name="description">

...是一组这样的规则:

// Both these inputs input will validate if 
// at least 1 input with class 'productinfo' is filled
partnumber: {
   require_from_group: [1,".productinfo"]
  }
description: {
   require_from_group: [1,".productinfo"]
}

第 3 项假设您.checked在成功验证后向错误消息中添加一个类。您可以按如下方式执行此操作,如此处所示

success: function(label) {  
        label.html(" ").addClass("checked"); 
}

在上面链接的演示中,我使用 CSS 给每个span.errorX 图像作为其背景,除非它有 class .checked,在这种情况下它会得到一个复选标记图像。

到目前为止,这是我的代码:

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
    var numberRequired = options[0];
    var selector = options[1];
    //Look for our selector within the parent form
    var validOrNot = $(selector, element.form).filter(function() {
         // Each field is kept if it has a value
         return $(this).val();
         // Set to true if there are enough, else to false
      }).length >= numberRequired;

    // The elegent part - this element needs to check the others that match the
    // selector, but we don't want to set off a feedback loop where each element
    // has to check each other element. It would be like:
    // Element 1: "I might be valid if you're valid. Are you?"
    // Element 2: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // Element 1: "Let's see. I might be valid if YOU'RE valid. Are you?"
    // ...etc, until we get a "too much recursion" error.
    //
    // So instead we
    //  1) Flag all matching elements as 'currently being validated'
    //  using jQuery's .data()
    //  2) Re-run validation on each of them. Since the others are now
    //     flagged as being in the process, they will skip this section,
    //     and therefore won't turn around and validate everything else
    //  3) Once that's done, we remove the 'currently being validated' flag
    //     from all the elements
    if(!$(element).data('being_validated')) {
    var fields = $(selector, element.form);
    fields.data('being_validated', true);
    // .valid() means "validate using all applicable rules" (which 
    // includes this one)
    fields.valid();
    fields.data('being_validated', false);
    }
    return validOrNot;
    // {0} below is the 0th item in the options field
    }, jQuery.format("Please fill out at least {0} of these fields."));

万岁!

喊出来

现在大喊大叫-最初,我的代码只是盲目地将错误消息隐藏在其他匹配字段上,而不是重新验证它们,这意味着如果还有其他问题(例如“只允许数字并且您输入了字母”) ,在用户尝试提交之前它一直隐藏。这是因为我不知道如何避免上面评论中提到的反馈循环。我知道一定有办法,所以我问了一个问题Nick Craver启发了我。谢谢,尼克!

已解决的问题

这最初是一个“让我分享一下,看看是否有人可以提出改进建议”之类的问题。虽然我仍然欢迎反馈,但我认为它在这一点上已经很完整了。(它可能更短,但我希望它易于阅读,不一定要简洁。)所以尽情享受吧!

更新 - 现在是 jQuery 验证的一部分

这已于2012 年 4 月 3 日正式添加到 jQuery 验证中。

4

9 回答 9

21

这是一个很好的解决方案内森。非常感谢。

这是使上述代码工作的一种方法,以防有人在集成它时遇到麻烦,就像我一样:

Additional-methods.js文件中的代码:

jQuery.validator.addMethod("require_from_group", function(value, element, options) {
...// Nathan's code without any changes
}, jQuery.format("Please fill out at least {0} of these fields."));

// "filone" is the class we will use for the input elements at this example
jQuery.validator.addClassRules("fillone", {
    require_from_group: [1,".fillone"]
});

html文件中的代码:

<input id="field1" class="fillone" type="text" value="" name="field1" />
<input id="field2" class="fillone" type="text" value="" name="field2" />
<input id="field3" class="fillone" type="text" value="" name="field3" />
<input id="field4" class="fillone" type="text" value="" name="field4" />

不要忘记包含 additional-methods.js 文件!

于 2009-09-27T07:27:52.797 回答
6

很好的解决方案。但是,我遇到了其他必需规则不起作用的问题。对表单执行 .valid() 为我解决了这个问题。

if(!$(element).data('being_validated')) {
  var fields = $(selector, element.form);
  fields.data('being_validated', true); 
  $(element.form).valid();
  fields.data('being_validated', false);
}
于 2011-05-02T06:08:55.857 回答
4

谢谢肖恩。这解决了我忽略其他规则的代码的问题。

我还进行了一些更改,以便“请填写至少 1 个字段..”消息显示在单独的 div 中,而不是在每个字段之后显示。

输入表单验证脚本

showErrors: function(errorMap, errorList){
            $("#form_error").html("Please fill out at least 1 field before submitting.");
            this.defaultShowErrors();
        },

在页面的某处添加这个

<div class="error" id="form_error"></div>

添加到 require_from_group 方法 addMethod 函数

 if(validOrNot){
    $("#form_error").hide();
}else{
    $("#form_error").show();
}
......
}, jQuery.format(" &nbsp;(!)"));
于 2011-06-09T20:09:23.440 回答
4

我已经提交了一个不受当前版本问题影响的补丁(因此“必需”选项在其他字段上停止正常工作,有关当前版本问题的讨论在github 上

http://jsfiddle.net/f887W/10/上的示例

jQuery.validator.addMethod("require_from_group", function (value, element, options) {
var validator = this;
var minRequired = options[0];
var selector = options[1];
var validOrNot = jQuery(selector, element.form).filter(function () {
    return validator.elementValue(this);
}).length >= minRequired;

// remove all events in namespace require_from_group
jQuery(selector, element.form).off('.require_from_group');

//add the required events to trigger revalidation if setting is enabled in the validator
if (this.settings.onkeyup) {
    jQuery(selector, element.form).on({
        'keyup.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusin) {
    jQuery(selector, element.form).on({
        'focusin.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.click) {
    jQuery(selector, element.form).on({
        'click.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

if (this.settings.onfocusout) {
    jQuery(selector, element.form).on({
        'focusout.require_from_group': function (e) {
            jQuery(selector, element.form).valid();
        }
    });
}

return validOrNot;
}, jQuery.format("Please fill at least {0} of these fields."));
于 2013-07-11T22:22:22.387 回答
3

在 PHP 中需要以 $ 开头的变量名,但在 Javascript 中非常奇怪(恕我直言)。另外,我相信您将其称为“$module”两次,一次称为“module”,对吗?看来这段代码不应该工作。

另外,我不确定它是否是正常的 jQuery 插件语法,但我可能会在您的 addMethod 调用上方添加注释,解释您完成的工作。即使有上面​​的文本描述,也很难遵循代码,因为我不熟悉字段集、:fill、值、元素或选择器所指的内容。对于熟悉 Validate 插件的人来说,其中大部分内容可能是显而易见的,因此请判断什么是正确的解释量。

也许您可以分解出一些变量来对代码进行自我记录;喜欢,

var atLeastOneFilled = module.find(...).length > 0;
if (atLeastOneFilled) {
  var stillMarkedWithErrors = module.find(...).next(...).not(...);
  stillMarkedWithErrors.text("").addClass(...)

(假设我确实理解了这些代码块的含义!:))

实际上,我不确定“模块”是什么意思——你可以给这个变量一个更具体的名字吗?

不错的代码,总的来说!

于 2009-08-20T14:18:33.510 回答
3

这是我对 Rocket Hazmat 答案的破解,试图解决其他定义字段也需要验证的问题,但在成功填写一个字段时将所有字段标记为有效。

jQuery.validator.addMethod("require_from_group", function(value, element, options){
    var numberRequired = options[0],
    selector = options[1],
    $fields = $(selector, element.form),
    validOrNot = $fields.filter(function() {
        return $(this).val();
    }).length >= numberRequired,
    validator = this;
    if(!$(element).data('being_validated')) {
        $fields.data('being_validated', true).each(function(){
            validator.valid(this);
        }).data('being_validated', false);
    }
    if (validOrNot) {
    $(selector).each(function() {
            $(this).removeClass('error');
            $('label.error[for='+$(this).attr('id')+']').remove();
        });
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));

现在唯一剩下的问题是字段为空,然后填充,然后再次为空的边缘情况......在这种情况下,错误将应用于单个字段而不是组。但这似乎不太可能以任何频率发生,并且在这种情况下它在技术上仍然有效。

于 2013-02-26T00:31:42.443 回答
2

因为我正在处理的表单有几个带有像这样的分组输入的克隆区域,所以我向 require_from_group 构造函数传递了一个额外的参数,恰好更改了 addMethod 函数的一行:

var commonParent = $(element).parents(options[2]);

这样一个选择器、ID 或元素名称可以传递一次:

jQuery.validator.addClassRules("reqgrp", {require_from_group: [1, ".reqgrp", 'fieldset']});

并且验证器会将验证限制为仅在每个字段集中具有该类的元素,而不是尝试计算表单中的所有 .reqgrp 分类元素。

于 2010-03-01T23:11:27.000 回答
1

我遇到了其他规则未与此一起检查的问题,因此我进行了更改:

fields.valid();

对此:

var validator = this;
fields.each(function(){
   validator.valid(this);
});

我还做了一些(个人)改进,这是我正在使用的版本:

jQuery.validator.addMethod("require_from_group", function(value, element, options){
    var numberRequired = options[0],
    selector = options[1],
    $fields = $(selector, element.form),
    validOrNot = $fields.filter(function() {
        return $(this).val();
    }).length >= numberRequired,
    validator = this;
    if(!$(element).data('being_validated')) {
        $fields.data('being_validated', true).each(function(){
            validator.valid(this);
        }).data('being_validated', false);
    }
    return validOrNot;
}, jQuery.format("Please fill out at least {0} of these fields."));
于 2012-02-08T22:44:35.120 回答
0

谢谢,内森。你为我节省了大量时间。

但是,我必须注意到这条规则还没有 jQuery.noConflict() 准备好。因此,必须用 jQuery 替换所有 $ 才能使用,比如说,var $j = jQuery.noConflict()

我有一个问题:我如何让它表现得像内置规则?例如,如果我输入电子邮件,“请输入有效的电子邮件”消息会自动消失,但如果我填写组字段之一,错误消息会保留。

于 2010-05-12T12:41:13.213 回答