給jquery-validation插件添加控件的驗證回調方法

  jquery-validation.js在前端驗證中使用起來很是方便,提供的功能基本上能知足大部分驗證需求,例如:一、內置了不少經常使用的驗證方法;二、能夠自定義錯誤顯示信息;三、能夠自定義錯誤顯示位置;四、能夠自定義驗證方法;五、ajax提交驗證,等等前端

  可是有時候,咱們在作項目的時候總會遇到一些特殊需求,例如,在單個控件驗證結束後,根據驗證的成功與否,須要調用一些本身定義的方法,這個需求貌似該插件沒有提供(可能有隻是我沒發現),沒辦法, 只能看源碼(這就是開源的好處啊),經過對源碼的分析,找到了一種能夠給指定控件添加驗證回調函數的方法,雖然須要修改一部分源碼,可是絲絕不影響對其以前的使用,該方法能夠批量添加多個控件的驗證回調函數,添加方式與添加自定義規則、自定義錯誤信息等相似,在閱讀源碼的過程當中,還發現瞭如何控制控件驗證的事件觸發,以及如何解決與My97DatePicker日期插件的衝突等問題,因此建議你們多看源碼,有時候會有意外收穫哦。jquery

  所以,本文包括三個方面:程序員

  1. 添加控件的驗證回調函數
  2. 控制控件驗證的事件觸發
  3. 解決與My97DatePicker日期插件的衝突問題

     下面咱們來一步步分析:ajax

給指定控件添加自定義回調函數

  首先,要添加回調函數必須找到該插件的驗證方法。其實在使用該插件的時候,從直觀的操做上咱們就能夠發現,控件驗證的觸發有多種方式,包括:控件焦點的失去,控件內容的改變(實際上是keyup),以及點擊提交按鈕等,相信你們都知道,這些只是表象。經查看源碼,能觸發驗證的方法有不少,如:validate、form、checkForm和element等,但這依舊是表象,以咱們程序員的嗅覺,真正的驗證函數確定只有一個,通過深刻勘察,前面每一個方法都調用了check方法,在check方法中發現一句話:app

var result = $.validator.methods[method].call(this, element.value.replace(/\r/g, ""), element, rule.parameters);

可見,這句代碼就是真正調用驗證邏輯的地方,所以,咱們須要將自定義的驗證回調函數,放置在check方法中,修改後的代碼以下(黃色部分爲添加的代碼):ide

check: function(element) {
                element = this.validationTargetFor(this.clean(element));

                var rules = $(element).rules();
                var dependencyMismatch = false;
                for (var method in rules) {
                    var rule = { method: method, parameters: rules[method] };
                    try {
                        var result = $.validator.methods[method].call(this, element.value.replace(/\r/g, ""), element, rule.parameters);

                        // if a method indicates that the field is optional and therefore valid,
                        // don't mark it as valid when there are no other rules
                        if (result == "dependency-mismatch") {
                            dependencyMismatch = true;
                            continue;
                        }
                        dependencyMismatch = false;

                        if (result == "pending") {
                            this.toHide = this.toHide.not(this.errorsFor(element));
                            return;
                        }

                        if (!result) {
                            this.formatAndAdd(element, rule);
                 //驗證失敗回調函數                   if ("invalidEventForElement" in this.settings) {
                    if (element.id in this.settings.validCallbackForElement) {     this.settings.validCallbackForElement[element.id].fail();     }                 }
return false; } } catch (e) { this.settings.debug && window.console && console.log("exception occured when checking element " + element.id + ", check the '" + rule.method + "' method", e); throw e; } } if (dependencyMismatch) return; if (this.objectLength(rules)) this.successList.push(element);
         //驗證成功回調函數
          
if ("invalidEventForElement" in this.settings) {
            
if (element.id in this.settings.validCallbackForElement) { this.settings.validCallbackForElement[element.id].success(); }
          }           
return true; },

 在這裏,爲了和插件的其餘自定義屬性保持一致,咱們定義了一個對象,結構以下:函數

validCallbackForElement:{
  yourControlId1:{
       success:function(){
                 //驗證成功回調
           },
           fail:function(){
                //驗證失敗回調
           }
      },
    yourControlId2:{
       success:function(){
                 //驗證成功回調
           },
           fail:function(){
                //驗證失敗回調
           }
      },
}        

而後將其放入validate方法的參數中,示例以下(修改的時候將"yourControlId1"和"yourControlId2"更換成你本身的控件id):ui

$("#form1").validate({
                rules: {
                    yourControlId1: {
                        required: true
                    },
                    yourControlId2: {
                        required: true,
                        maxlength: 500
                    }
                }, //end rule
                messages: {
                    yourControlId1: {
                        required: "不能爲空"
                    },
                    yourControlId2: {
                        required: "不能爲空",
                        maxlength: "內容過長"
                    }
                },
                errorPlacement: function(error, element) {
                    if (element.context.name == "yourControlId1") {
                        error.appendTo(document.getElementById("yourControlId1_error"));
                    }
                    else if (element.context.name == "yourControlId2")
                        error.appendTo(document.getElementById("yourControlId2_error"));
                },
                submitHandler: function(form) {
                    ajaxSubmit();
                },
                validCallbackForElement: {
                    yourControlId1: {
                        success: function() {
                            //控件yourControlId1的驗證成功回調
                        },
                        fail: function() {
                            //控件yourControlId1的驗證失敗回調
                        }
                    },
                    yourControlId2: {
                        success: function() {
                            //控件yourControlId2的驗證成功回調
                        },
                        fail: function() {
                            //控件yourControlId2的驗證失敗回調
                        }
                    }
                }
            }); //end validate function

在構造函數中,會將validCallbackForElement對象合併多this.settings對象中,所以,在調用的時候須要寫成:this.settings.validCallbackForElement[element.id].success();this

由於咱們不必定給全部的驗證控件都添加回調函數,所以,在調用的時候須要首先判斷該控件有沒有對應的毀掉函數,這樣,調用代碼就改成:spa

if ("invalidEventForElement" in this.settings) {
  if (element.id in this.settings.validCallbackForElement) {   this.settings.validCallbackForElement[element.id].fail();   }
}

到這裏,如何給指定控件添加自定義驗證回調函數的問題就已經解決了。下面提供一個簡單的示例:

添加控件驗證回調方法的示例程序

驗證觸發問題

在使用jquery-validation的時候發現,在調用驗證方法以前,控件的keyup和focusout事件是不能觸發驗證的,好比咱們打開一個頁面,先不點擊提交按鈕,這樣就不對錶單進行過驗證,這時候咱們把光標放在input控件上,而後什麼也不寫,在讓該input失去焦點,此時咱們會發現,並無提示該input是必填項的錯誤信息,緣由在於,jquery-validation插件在驗證表單的時候會將錯誤信息保存在errorMap這個變量中,errorMap的結構以下:

errorMap{
     yourControlId1:"必填項",
     yourControlId2:"內容過長",
     ......
}

而後將errorMap合併到this.submited對象中,在focusout和keyup事件中,會判斷this.submited中是否有該控件的id,若是有才會執行驗證,代碼以下:

onfocusout: function(element, event) {
if (!that.checkable(element) && (element.name in that.submitted || !that.optional(element))) {
that.element(element);
}
},
onkeyup: function(element, event) {
 if (element.name in this.submitted || element == this.lastElement) { 
this.element(element);
}
},

所以,在執行表單驗證後纔會激活控件的focusout和keyup驗證,若是你想一開始就能使focusout和keyup觸發驗證,能夠在這裏去掉紅色部分的代碼

以前對這一部分的描述有問題,僅僅去掉紅色部分的代碼並不能達到驗證的目的,從源碼中能夠看出,jquery-validation插件的做者對這裏的控制仍是比較嚴格的,不只依賴submitted屬性,還要根據控件的值來作相應判斷,可能這款插件的意圖就是保證控件在沒有值而且沒有進行表單驗證以前不調用控件的驗證方法,這點經過onfocusout和onkeyup事件中的邏輯就能看出來,兩處的邏輯不太同樣。所以,若是想要在表單驗證前就能對空值的控件經過focusout和keyup觸發驗證方法,那麼就須要修改不少地方,這顯然不符合該插件的編寫思路。

與My97DatePicker日期控件的衝突問題

     相信My97DatePicker是不少人都用過的一個日期控件,該控件會與jquery-validation插件衝突,致使jquery-validation插件的focusout事件沒法觸發驗證,多是由於這兩個東西都註冊了focusout事件,因此致使了衝突,原本想看看My97DatePicker的源碼,打開以後發現是壓縮版的,好像還進行了混淆,因此若是在頁面中同時使用了這兩個東西的話,只能修改jquery-validation了,能夠進行以下修改:

onfocusout: function(element, event) {
                //修改了此處代碼,不然會與My97DatePicker衝突,致使失去焦點的時候不會驗證
                var that = this;
                setTimeout(function() {
                    if (!that.checkable(element) && (element.name in that.submitted || !that.optional(element))) {
                        that.element(element);
                    }
                }, 100);

            },
相關文章
相關標籤/搜索