研究了一下Asp.net Validator Control在Client Side的实现:

1. Page 在 Browser load完后会依次执行:
 1) var Page_Validators =  new Array(document.getElementById("RequiredFieldValidator1"), ...);
     var Page_ValidationSummaries =  new Array(document.getElementById("ValidationSummary1"));
 2) validator = document.all ? document.all["RequiredFieldValidator1"] : document.getElementById("RequiredFieldValidator1");
    .controltovalidate = "TextBox1";
    .focusOnError = "t";
    .errormessage = "Required";
    .display = "Dynamic";
    .evaluationfunction = "RequiredFieldValidatorEvaluateIsValid";
    .clientvalidationfunction = "validateTA";
    .initialvalue = "";
    .maximumvalue = "100";
    .minimumvalue = "1";
    .controltocompare = "TextBox3";
    .validationexpression = "testGP";
    .validationGroup = "^\\d{3}$";

注意: 这里的validator仅仅是页面众多的验证控件之一(即页面会初始化很多的这么个些validator),这里列举仅作为一个实例来研究。另外,以上列举中的附加属性,是合计所有验证控件常见的属性,而不是每个validator都有以上全部属性(更准确说,每个validator仅拥有其中的不同部分)。
 3) var Page_ValidationActive = false;
 4) call ValidatorOnLoad()
    --> val:
    .evaluationfunction
    .isvalid
    .enabled
    .controltovalidate *   
    .controlhookup    *
    --> Page_ValidationActive = true;


2. 跟踪Page中执行的ValidatorOnLoad方法到Asp.net动态生成的Resource文件(Resource1),依次会执行:
 ValidatorOnLoad(): 
    --> ValidatorHookupControlID(val.controltovalidate, val) / ValidatorHookupControlID(val.controlhookup, val)
      --> ValidatorHookupControl(ctrl, val)
        --> ValidatorHookupEvent(control, eventType, functionPrefix)
             *** ValidatedControlOnBlur(event) <--> Page_InvalidControlToBeFocused
             *** ValidatorOnChange(event)  <--> ValidatorValidate(vals[i], null, event) * + ValidatorUpdateIsValid() *
             *** ValidatedTextBoxOnKeyPress(event) <--> ValidatorOnChange(event) + AllValidatorsValid(vals) *
 
 ValidatorValidate(val, validationGroup, event):
    --> IsValidationGroupMatch(val, validationGroup);
    --> val.isvalid = val.evaluationfunction(val);
                 <--> RequiredFieldValidatorEvaluateIsValid
                 <--> RangeValidatorEvaluateIsValid
                 <--> RegularExpressionValidatorEvaluateIsValid
                 <--> CompareValidatorEvaluateIsValid
                 <--> CustomValidatorEvaluateIsValid
    --> Page_InvalidControlToBeFocused == null ?
    --> ValidatorSetFocus(val, event); *
    --> ValidatorUpdateDisplay(val); *
 ValidatorUpdateIsValid():
    --> Page_IsValid = AllValidatorsValid(Page_Validators);
 AllValidatorsValid(validators):
    --> return true/false;

 IsValidationGroupMatch(control, validationGroup):
    --> return (controlGroup == validationGroup);
 ValidatorSetFocus(val, event):
    --> IsInVisibleContainer(ctrl) ?
    --> ctrl.focus() + Page_InvalidControlToBeFocused = ctrl;
 ValidatorUpdateDisplay(val):
    --> val.style.display = val.isvalid ? "none" : "inline";
    --> val.style.visibility = val.isvalid ? "hidden" : "visible";

 RequiredFieldValidatorEvaluateIsValid(val):
    --> return (ValidatorTrim(ValidatorGetValue(val.controltovalidate)) != ValidatorTrim(val.initialvalue)) *
 RangeValidatorEvaluateIsValid(val):
    --> value = ValidatorGetValue(val.controltovalidate);
    --> ValidatorTrim(value);
    --> ValidatorCompare(value, val.minimumvalue, "GreaterThanEqual", val) && ValidatorCompare(value, val.maximumvalue, "LessThanEqual", val)
 RegularExpressionValidatorEvaluateIsValid(val):
    --> value = ValidatorGetValue(val.controltovalidate);
    --> ValidatorTrim(value);
    --> rx = new RegExp(val.validationexpression);
    --> matches = rx.exec(value);
 CompareValidatorEvaluateIsValid(val):
    --> value = ValidatorGetValue(val.controltovalidate);
    --> ValidatorTrim(value);
    --> ValidatorCompare(value, compareTo, operator, val);
 CustomValidatorEvaluateIsValid(val):
    --> value = ValidatorGetValue(val.controltovalidate);
    --> eval(val.clientvalidationfunction + "(val, args) ;");
    --> return args.IsValid;

 ValidatorTrim(s):
    --> var m = s.match(/^\s*(\S+(\s+\S+)*)\s*$/);
    --> return (m == null) ? "" : m[1];
 ValidatorGetValue(id):
    --> return control.value;
    --> return ValidatorGetValueRecursive(control); *
 ValidatorCompare(operand1, operand2, operator, val):
    --> ValidatorConvert(operand1, dataType, val) *
    --> return op1 !<>= op2;

 ValidatorGetValueRecursive(control):
    --> return control.value;
    --> return val = ValidatorGetValueRecursive(control.childNodes[i]);
 ValidatorConvert(op, dataType, val):
    --> dataType == "Integer/Double/Currency/Date" ?
    --> return (isNaN(num) ? null : num); (*)

注意:以上2部分仅仅是页面加载过程中,通过js为页面中的control和validator建立关联,并且绑定相关client event,而没有做任何validation。


3. 回到页面源码,查看页面剩下的重要部分:
 1) <form name="form1" method="post" action="default2.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="form1">
 2) var theForm = document.forms['form1'] ? document.forms['form1'] : document.form1;
 3) <input type="submit" name="btnOK" value="submit" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnOK", "", true, "testGP", "", false, false))" id="btnOK" />

 4)function __doPostBack(eventTarget, eventArgument) {
      if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
          theForm.__EVENTTARGET.value = eventTarget;
          theForm.__EVENTARGUMENT.value = eventArgument;
          theForm.submit();
      }
 }
 function WebForm_OnSubmit() {
  if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false) return false;
  return true;
 }
 function ValidatorOnSubmit() {
      if (Page_ValidationActive) {
          return ValidatorCommonOnSubmit();
      }
      else {
          return true;
      }
 }
从3)中input(1个button)的以下部分开始跟踪,依次执行:

onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("btnOK", "", true, "testGP", "", false, false))" -->
 WebForm_DoPostBackWithOptions(options):
    --> WebForm_PostBackOptions options; *
    --> validationResult = Page_ClientValidate(options.validationGroup); [*--> Resource1]
    --> theForm.action = options.actionUrl;
    --> lastFocus = theForm.elements["__LASTFOCUS"]; [?]
       lastFocus.value = options.eventTarget / active.id / active.name;
    --> __doPostBack(options.eventTarget, options.eventArgument); 
 WebForm_PostBackOptions(eventTarget, eventArgument, validation, validationGroup, actionUrl, trackFocus, clientSubmit):
    --> this.eventTarget = eventTarget;
    --> ...

 Page_ClientValidate(validationGroup):
    --> Page_InvalidControlToBeFocused = null;
    --> ValidatorValidate
    --> ValidatorUpdateIsValid
    --> ValidationSummaryOnSubmit(validationGroup); *
    --> Page_BlockSubmit = !Page_IsValid;
    --> return Page_IsValid;
 ValidationSummaryOnSubmit(validationGroup):
    --> Page_ValidationSummaries
    --> Page_IsValid
    --> IsValidationGroupMatch
    --> summary.displaymode == "List/BulletList/SingleParagraph"

可以看到,如果点击button,validators如果发现invalid部分,显示相关invalid messages。然后执行form的submit方法。跟踪

onsubmit="javascript:return WebForm_OnSubmit();" 依次执行:

 WebForm_OnSubmit():
    --> return ValidatorOnSubmit();
    --> Page_ValidationActive
    --> ValidatorCommonOnSubmit(); * 
 ValidatorCommonOnSubmit():
    --> Page_InvalidControlToBeFocused = null;
    --> result = !Page_BlockSubmit;   *******<Page_BlockSubmit>********
    --> window.event.returnValue = result;
    --> Page_BlockSubmit = false;

从上可以看到:form根据validators判断结果的另外一个全局变量Page_BlockSubmit来做一些事情,取消页面提交。如果页面IsValid,会执行__doPostBack方法,相关参数也被PostBack到Server Side,一个新的Asp.net生命周期就会开始(这是Server端的事情了,不再详叙)。

 

附件:https://files.cnblogs.com/Langzi127/Asp.netValidator.zip