jQuery Validate 和 Masked Input 之间的冲突

Posted

技术标签:

【中文标题】jQuery Validate 和 Masked Input 之间的冲突【英文标题】:Conflict between jQuery Validate and Masked Input 【发布时间】:2011-05-22 21:29:06 【问题描述】:

我有一个表格,它同时使用 jQuery Validate 和 Masked Input 作为电话号码和美国邮政编码字段。

例如,对于美国邮政编码,Masked Input 仅允许输入数字,并强制采用“99999”或“99999-9999”格式(其中 9 可以是任何数字)。验证规则要求相同。但在某些情况下,Validate 会在字段实际上应该有效时将其标记为无效。

代码细节

jQuery Validate 在邮政编码字段上使用的正则表达式是^\d5$|^\d5\-\d4$

我使用 Masked Input 应用的掩码是 .mask('99999?-9999')

重现步骤

当我执行以下操作时,它们之间会产生冲突:

填写无效的邮政编码(例如,三位数字) 远离场地。 Masked Input 擦除输入(预期行为),jQuery Validate 将其标记为无效,因为它是空白的(也是预期的)。 返回 zip 字段并填写有效的 5 位 zip。 远离场地。 它仍然被标记为无效。这是意料之外的

如果我填写 9 位数的 zip,则不会发生此问题。

假设

我认为这个错误是因为在 5 位 zip 的情况下,Masked Input 临时插入了“-____”以向用户显示他们可以选择输入一个破折号和另外四位数字。这会在模糊时被删除,但在被删除之前,该字段会被验证并失败,因为不允许使用下划线。

这一假设得到以下事实的支持:如果表单随后重新验证,则 zip 字段将通过。我有两种方法:

通过提交表单;单击提交按钮、zip 字段通过并提交表单时,所有字段都会重新验证。

通过设置重新验证该特定字段的blur 事件。例如:

$("#zipcode").blur(function() $(this).closest('form').validate().element($(this)); );

这可以作为一个hacky解决方案,但不是很令人满意,因为1)默认设置已经在模糊上重新验证,所以这是重复的,2)它需要额外的代码除了正常的验证规则。

还有其他人遇到过这个问题吗? 您有比设置额外的模糊事件侦听器更优雅的解决方案吗?

更新:应用 hacky 解决方案

即使应用上面的 hacky 解决方案也没有我想要的那么好。例如,这不起作用:

$appDiv.delegate('input,select,textarea','blur',function()
  $(this).closest('form').validate().element($(this));
);

...这也不是:

$('input,select,textarea').live('blur',function()
  $(this).closest('form').validate().element($(this));
);

...但确实如此:

$('input,select,textarea').each(function()
  $(this).blur(function()
    $(this).closest('form').validate().element($(this));
  );
);

由于这些元素是由 AJAX 加载的,因此每次加载表单部分时都必须运行 .each 版本。

【问题讨论】:

真棒问题 - 深思熟虑并提出。 【参考方案1】:

我尝试了以下解决方案,它就像一个魅力。

$("[data-input-type=phone]", "body")
  .mask("(999) 999 99 99")
  .bind("blur", function () 
    // force revalidate on blur.

    var frm = $(this).parents("form");
    // if form has a validator
    if ($.data( frm[0], 'validator' )) 
      var validator = $(this).parents("form").validate();
      validator.settings.onfocusout.apply(validator, [this]);
    
  );

触发问题的是事件排序。 当一个元素被屏蔽时,它由 blur 上的 maskedinput 插件验证,但相同的元素由 focusout 事件(它是非 ie 浏览器上模糊的包装器)上的验证器插件验证,该事件在 maskedinput 模糊之前调用。

在这种情况下,当验证器检查值时,输入元素的值是"(___) ___ __ __"。当代码到达 maskedinput 的 blur 事件时,插件会测试并清除该值,因为它不是有效的输入。

每个验证案例的验证结果可能不同。例如required 规则将成功通过,因为元素有一个值。即使我们将输入留空,非必需的 number 字段也会失败,因为像 "999" 这样的掩码可能会被测试为 12_

上面的代码测试了屏蔽输入的形式是否附加了验证,并调用了 focusout 事件处理程序。由于我们的处理程序是最新的,希望它最终会被调用。

警告,代码只是复制验证插件的行为。它可能会工作十年,但如果验证插件决定做与现在不同的事情,它可能会失败。

真诚的

【讨论】:

【参考方案2】:

我实际上是在寻找这个确切问题的答案。最终找到了一个更可靠的解决方法。

由于我为邮政编码定义了自己的验证器方法,因此我对其进行了修改,以便在我从值中删除占位符后,如果邮政编码的长度为 6,它会删除连字符。

看起来像这样:

$.validator.addMethod("zipcode", function(postalcode, element) 
            //removes placeholder from string
            postalcode = postalcode.split("_").join("");

            //Checks the length of the zipcode now that placeholder characters are removed.
            if (postalcode.length === 6) 
                //Removes hyphen
                postalcode = postalcode.replace("-", "");
            
            //validates postalcode.
            return this.optional(element) || postalcode.match(/^\d5$|^\d5\-\d4$/);
        , "Please specify a valid zip code");

因此,如果输入的邮政编码只有 5 个数字(包括连字符在内的 6 个),我正在验证的邮政编码将由输入插件添加占位符,并删除连字符。所以它会正确地验证它。

【讨论】:

这是一个不错的解决方案。我可能需要一段时间才能尝试,但这是我见过的最好的建议。谢谢! postalcode = postalcode.split("_").join(""); 可能是postalcode = postalcode.replace(/_/g, "");【参考方案3】:

只需挂接到 mask() 函数,并在默认蒙版的模糊逻辑之后附加一些额外的逻辑来触发您自己的模糊逻辑:

//Store the original mask function
var origMaskFn = $.fn.mask;

$.fn.mask = function (mask, settings) 

    //Call the original function applying the default settings
    origMaskFn.apply(this, [mask, settings]);

    //Manually validate our element on blur to prevent unobtrusive messages
    //from showing before the mask is properly scrubbed
    $(this).bind('blur', function () 
        var $form = $(this).parents('form');
        if (!$form.exists())
            return;

        var validator = $form.validate().element($(this));
    );

此外,您可能会注意到“exists()”函数。这是我在 jQuery 选择器中经常使用的东西:

//Function to detect if a jQuery elements exists.  i.e.:
//      var $element = $(selector);
//      if($element.exists()) do something...

$.fn.exists = function () 
    return this.length !== 0;

【讨论】:

【参考方案4】:

我有一个类似的问题并设法解决它,将默认占位符更改为空字符串。

我使用的是 .mask('9?99'); 并使用简单的“数字”规则进行验证,并且遇到了同样的冲突。

我将掩码声明更改为.mask('9?99',placeholder:''); 并且...不再有冲突。 :)

您的情况可能存在问题,因为邮政编码中的- 始终由掩码插件插入表单中。我认为您可以将正则表达式更改为^\d5\-$|^\d5\-\d4$(在两种情况下都匹配破折号),然后它应该验证。

无论如何,您之前发布了一段时间,可能不再需要这个,但也许它会帮助其他人。 :)

【讨论】:

只要仔细阅读 Aliester 的回答,它的作用基本相同。这在某种程度上可能更“干净”,但并没有消除连字符。 这个解决方案当然会随着下划线消失,但它有效!! 这是对我的修复。我在输入掩码中使用了占位符。我只是将其设置为黑色并将占位符放在我的 html.TextboxFor 上,或者基本上放在输入字段上,而不是通过输入掩码。【参考方案5】:

绑定的顺序是否重要?比如有没有区别

$(function() 
  var $form = $("#myForm"), $zip = $("#zip");
  $zip.mask("99999?-9999");
  $form.validate();
);

$(function() 
  var $form = $("#myForm"), $zip = $("#zip");
  $form.validate();
  $zip.mask("99999?-9999");
);

我认为绑定应该按顺序触发。

【讨论】:

好主意,但不,在这种情况下交换订单没有任何区别。该死,我以为就是这样。 :)【参考方案6】:

我尝试了一些解决方案,但没有一个对我有用,我正在使用“bootstrapValidator”(http://bootstrapvalidator.com/)和“jquery.maskedinput”(http://digitalbush.com/projects/masked-input-plugin/)所以如果有人仍然有这个问题,我的解决方案是:

我的输入是这样标记的:

<input type="text" class="form-control" id="telefone" placeholder="(__) ____-_____" name="telefone" required>

完整的脚本如下:

$(document).ready(function() 
   var telefone = $('#telefone'), //THAT'S MY INPUT WITH MASK
       form = telefone.parents("form"), //THAT'S MY FORM WITH THE VALIDATE
       alteredField = false; //THAT'S A FLAG TO SSE IF THE INPUT WAS ALTERED

            // APPLY MY MASK
            telefone.mask('(99) 9999-9999?9');

            // FOCUS ON ANY INPUT
            form.find('input[type=text]').focus(function()
                // REMOVE THE VALIDATION STATUS
                $(form).data('bootstrapValidator').updateStatus($(this).attr('name'), 'NOT_VALIDATED');
                // ENABLE THE SUBMIT BUTTON
                $(form).data('bootstrapValidator').disableSubmitButtons(false);
            );

            // FOCUS ON THE MASKED INPUT
            telefone.focus(function()
                // DISABLE THE VALIDATE
                $(form).data('bootstrapValidator').enableFieldValidators('telefone', false);
                // ENABLE THE SUBMIT BUTTON
                $(form).data('bootstrapValidator').disableSubmitButtons(false);
            ).blur(function() // BLUR ON THE MASKED INPUT
                // GET THE INPUT VALUE
                var value = telefone.val();
                // ENABLE THE VALIDATE 
                $(form).data('bootstrapValidator').enableFieldValidators('telefone', true);
                // CHECK IF THE VALUE IS EMPTY
                if(value != "")
                    // CHANGE THE STATUS OF THE INPUT TO VALID 
                    $(form).data('bootstrapValidator').updateStatus('telefone', 'VALID')
                    // ACTIVE THE FLAG
                    alteredField = true;
                else if (alteredField)  // IF THE INPUT WAS ALTERED BEFORE AND DON'T HAVE VALUE
                    // CHANGE THE STATUS OF THE INPUT TO INVALID
                    $(form).data('bootstrapValidator').updateStatus('telefone', 'INVALID')
                ;
            );

        );

此代码修改了“bootstrapValidator”的标准行为,但这是我必须解决该错误的唯一方法。

我不知道性能问题,所以请修改和改进代码^^ 希望对您有所帮助^_^

【讨论】:

【参考方案7】:

我遇到了同样的问题,我花了两个小时来制定正确的解决方案。最后,我做了一个适当的代码。你可以使用它

邮政编码。

$.validator.addMethod("zipcode", function(postalcode, element) 
            //removes placeholder from string
            postalcode = postalcode.replace(/[^0-9]/g, "");
            //validates postalcode.
            return this.optional(element) || postalcode.match(/^\d5$|^\d5\-\d4$/);
        , "Please specify a valid zip code");

如果您想增加或减少邮编长度,请更改 postalcode.match(/^\d5$|^\d5-\d4$/);根据您的要求编号。

对于电话,将使用相同的代码,但您只需更改数字,例如 postalcode.match(/^\d10$|^\d10-\d9$/);

$.validator.addMethod("phone_length", function(phoneno, element) 
                //removes all special characters
                phoneno= phoneno.replace(/[^0-9]/g, "");
                return this.optional(element) || phoneno.match(/^\d10$|^\d10\-\d9$/);
            , "Please enter a valid phone");

我希望这段代码对你有很大帮助。如果您想知道如何调用,只需在验证字段中添加 zipcode:true

【讨论】:

以上是关于jQuery Validate 和 Masked Input 之间的冲突的主要内容,如果未能解决你的问题,请参考以下文章

jquery.validate+jquery.form提交的三种方式

JQuery.validate remote怎么没用啊?

jquery validate 无效

怎么在asp.net mvc项目的script文件夹添加jquery.validate.min.js和jquery.validate.unobtrusive.min.js

JQuery.Validate使用消息添加动态规则

使用jquery.validate.js实现boostrap3的校验和验证