jQuery 验证和屏蔽输入之间的冲突

发布于 2024-10-07 10:42:15 字数 2008 浏览 0 评论 0原文

我有一个同时使用 jQuery Validate电话号码和美国邮政编码字段的屏蔽输入

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

代码细节

jQuery Validate 在邮政编码字段上使用的正则表达式是 ^\d{5}$|^\d{5}\-\d{4}$

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

重现步骤

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

  • 填写无效的邮政编码(例如,三位数字)
  • 按 Tab 键离开该字段。屏蔽输入会删除输入(预期行为),并且 jQuery Validate 将其标记为无效,因为它是空白的(也是预期行为)。
  • 返回邮政编码字段并填写有效的5 位邮政编码。
  • Tab 远离该字段。 它仍然被标记为无效。这是出乎意料的。

如果我填写 9 位邮政编码,则不会发生此问题。

假设

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

这一假设得到了以下事实的支持:如果随后重新验证表单,邮政编码字段将通过。我通过两种方式完成此操作:

  • 通过提交表单;单击提交按钮、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 版本部分已加载。

I've got a form that's using both jQuery Validate and Masked Input for phone number and US zip code fields.

For example, for a US zip code, Masked Input allows only numbers to be entered, and forces either the format "99999" or "99999-9999" (where 9 can be any number). The Validate rule requires the same. But in some cases, Validate marks a field as invalid when it should actually be valid.

Code Specifics

The regex that jQuery Validate is using on the zip code field is ^\d{5}$|^\d{5}\-\d{4}$.

The mask I'm applying with Masked Input is .mask('99999?-9999')

Steps to reproduce

A conflict between them is produced when I do the following:

  • Fill out an invalid zip code (say, three digits)
  • Tab away from the field. Masked Input erases the input (expected behavior) and jQuery Validate marks it as invalid because it's blank (also expected).
  • Go back to the zip field and fill in a valid 5-digit zip.
  • Tab away from the field. It is still marked as invalid. This is unexpected.

This problem does not happen if I fill out a 9-digit zip.

Hypothesis

I think this error is because in the case of the 5-digit zip, Masked Input has temporarily inserted "-____" to show the user that they can optionally enter a dash and four more digits. This is removed on blur, but before it's removed, the field is validated and fails, since underscores aren't permitted.

This hypothesis is supported by the fact that, if the form is subsequently re-validated, the zip field will pass. I have done this two ways:

  • By submitting the form; all fields are re-validated when the submit button is clicked, the zip field passes, and the form submits.

  • By setting up a blur event that re-validates that specific field. For example:

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

This can serve as a hacky solution, but isn't very satisfactory, because 1) the default settings already re-validate on blur, so this is repetitive, and 2) it requires additional code besides the normal Validate rules.

Has anybody else run into this issue? Do you have a more elegant solution than setting up an additional blur event listener?

Update: applying the hacky solution

Even applying the hacky solution above doesn't work as nicely as I'd like. For instance, this doesn't work:

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

...nor does this:

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

...but this does:

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

Since these elements are loaded by AJAX, the .each version has to be run each time a form section is loaded.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

恰似旧人归 2024-10-14 10:42:15

我尝试了以下解决方案,它很有魅力。

$("[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 事件上的验证器插件进行验证(这是非模糊的包装器)即浏览器),它在 maskedinput 的模糊之前调用。

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

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

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

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

真挚地

I tried following solution, and it works like a charm.

$("[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]);
    }
  });

What triggers the problem is event ordering.
When an element is masked, it is validated by maskedinput plugin on blur, but same element is validated by validator plugin on focusout event (which is a wrapper for blur on non-ie browsers) which is called before maskedinput's blur.

In this situation input element has the value "(___) ___ __ __" when validator checks for the value. When code reaches maskedinput's blur event, plugin tests and clears the value since it is not a valid input.

Validation result may be different for each validation case. For instance required rules will pass with success since element has a value. non-required number fields will fail even if we leave the input empty since a mask like "999" may be tested as 12_

the code above tests if form of masked input has validation attached to it, and recalls focusout event handler. Since our handler is attached as the latest, hopefully it will be called at last.

A warning, code simply copies behavior of validation plugin. It will probably work for a decade but may fail if validation plugin decides to do things different than now.

sincerely

囚我心虐我身 2024-10-14 10:42:15

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

由于我正在为邮政编码定义自己的验证器方法,因此我对其进行了修改,以便在从值中删除占位符后,如果邮政编码的长度为 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(/^\d{5}$|^\d{5}\-\d{4}$/);
        }, "Please specify a valid zip code");

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

I was actually looking for an answer to this exact question. Ended up figuring out a more reliable workaround.

Since I am defining my own validator method for the zipcode I modified it so that it would remove the hyphen if the length of the zipcode was 6 after I removed the placeholder from the value.

It looked like this:

$.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(/^\d{5}$|^\d{5}\-\d{4}$/);
        }, "Please specify a valid zip code");

So the postalcode I am validating will have the placeholders added by the input plugin and the hyphen removed if the zipcode that is entered only has 5 numerical digits (6 including the hyphen). So it will validate it properly.

安静被遗忘 2024-10-14 10:42:15

只需挂接到 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;
}

Simply hook in to the mask() function and append some additional logic to fire your own blur logic after the default mask's blur logic:

//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));
    });
}

Also, you may notice the 'exists()' function. This is something I use a lot with my jQuery selectors:

//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;
}
审判长 2024-10-14 10:42:15

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

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

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

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

不管怎样,您不久前发布了,可能不再需要这个,但也许它会对其他人有所帮助。 :)

I had a similar problem and managed to solve it changing the default placeholder character to an empty string.

I was using a .mask('9?99'); and was validating with a simple 'number' rule, and was having the same kind of conflict.

I changed the mask declaration to .mask('9?99',{placeholder:''}); and... no more conflict. :)

There's a possible problem in your case, because of the - in the zip code, which is always inserted in the form by the mask plugin. I think you can change the regexp to ^\d{5}\-$|^\d{5}\-\d{4}$ (matching the dash in both cases) and it should validate then.

Anyway, you posted a while ago and probably don't need this anymore but perhaps it'll help someone else. :)

庆幸我还是我 2024-10-14 10:42:15

绑定的顺序重要吗?例如,之间有区别吗

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

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

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

Does it matter the order that you do the binding? For example, is there a difference between

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

and

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

I think that the bindings should fire in order.

稚气少女 2024-10-14 10:42:15

我尝试了一些解决方案,但没有一个对我有用,我正在使用“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”的标准行为,但我必须解决这个错误的唯一方法。

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

I tried some of the solutions but none worked for me, i'm using 'bootstrapValidator' (http://bootstrapvalidator.com/) and 'jquery.maskedinput' (http://digitalbush.com/projects/masked-input-plugin/) so if someone still have this problem, my solution was:

My input was marked like this:

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

And the full script like this:

$(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')
                };
            });

        });

This code modifies the standard behavior of the 'bootstrapValidator', but was the only way that I gotta to solve the bug.

I don't know about performance issues, so please modify and improve the code ^^
Hope that this help ^_^

千纸鹤带着心事 2024-10-14 10:42:15

我遇到了同样的问题,我花了两个小时来制定正确的解决方案。最后,我编写了正确的代码。您可以将其

用于邮政编码。

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

如果您想增加或减少邮政编码长度,请更改 postalcode.match(/^\d{5}$|^\d{5}-\d{4}$/);数量根据您的要求。

对于手机,将使用相同的代码,但您只需更改邮政编码等数字。match(/^\d{10}$|^\d{10}-\d{9}$/);

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

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

I had faced same issue and I spend two hours to make a proper solution. Finally, I made a proper code. You can use it

For ZIP code.

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

If you want to increase or decrease zip length then change postalcode.match(/^\d{5}$|^\d{5}-\d{4}$/); number according to your requirement.

For phone same code will be used but you need to change only numbers like postalcode.match(/^\d{10}$|^\d{10}-\d{9}$/);

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

I hope this code will help you a lot. If you want to know how to call then simply add zipcode:true in your validation field

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文