手把手教你自己写一个js表单验证框架
其实我自己也就能简单用用js而已,但是呢,相对很多初学者来说多懂了点Know How所以斗胆孟浪一下,将一些所得记录下来,以供更多的初学者能够知道一个东西的实现过程,省去在源码里摸索的过程。
在表单程序中,在页面上需要很多的Js代码来验证表单,每一个field是否必须填写,是否只能是数字,是否需要ajax到远程验证,blablabla。如果一个一个单独写势必非常的繁琐,所以我们的第一个目标就是构建一个类似DSL的东西,用表述的语句而非控制语句来实现验证。
其次一个个单独写的话还有一个问题就是必须全部验证通过才能提交,但是单独验证会因为这个特征而增加很多额外的控制代码,且经常会验证不全面。所以第二个目标就是能够全面的整合整个验证的过程。
最后不能是一个无法扩展的一切写死的实现,必要的扩展性还是要的。
首先,我们需要一个能够描述对字段验证的类
1 2 3 4 5 6 7 | function Field(params){ this .field_id=params.fid; //要验证的字段的ID this .validators=params.val; //验证器对象数组 this .on_suc=params.suc; //当验证成功的时候执行的事件 this .on_error=params.err; //当验证失败的时候执行的事件 this .checked= false ; //是否通过验证 } |
关于验证器对象我们在后面来讨论,接下来我们扩展这个类,加入validate方法
1 2 3 4 5 6 7 8 9 10 11 | Field.prototype.validate= function (){ //循环每一个验证器 for (item in this .validators){ //给验证器附加验证成功和验证失败的回调事件 this .set_callback( this .validators[item]); //执行验证器上的Validate方法,验证是否符合规则 if (! this .validators[item].validate( this .data())){ break ; //一旦任意一个验证器失败就停止 } } } |
再加入一个获取字段值的方法:
1 2 3 4 | //获取字段值的方法 Field.prototype.data= function (){ return document.getElementById( this .field_id).value; } |
设置验证器回调函数的方法set_callback如下:
1 2 3 4 5 6 7 8 9 10 11 | Field.prototype.set_callback= function (val){ var self= this ; //换一个名字来存储this,不然函数的闭包中会覆盖这个名字 val.on_suc= function (){ //验证成功执行的方法 self.checked= true ; //将字段设置为验证成功 self.on_suc(val.tips); //执行验证成功的事件 } val.on_error= function (){ //验证失败的时候执行的方法 self.checked= false ; //字段设置为验证失败 self.on_error(val.tips); //执行验证失败的事件 } } |
接下来我们就来看看验证器,验证器是真正执行验证过程的类,根据一般的验证过程,我们可以将其分类成,长度验证(包括必填验证),正则表达式验证,自定义函数验证,Ajax远程验证这几种,所以我们定义这几种验证器类,Ajax远程验证为了方便引用了jQuery,其他部分也有用到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | //长度验证的验证器类 function Len_val(min_l,max_l,tip){ this .min_v=min_l; this .max_v=max_l; this .tips=tip; this .on_suc= null ; this .on_error= null ; } Len_val.prototype.validate= function (fd){ if (fd.length< this .min_v||fd.length> this .max_v){ this .on_error(); return false ; } this .on_suc(); return true ; } //正则表达式验证器 function Exp_val(expresion,tip){ this .exps=expresion; this .tips=tip; this .on_suc= null ; this .on_error= null ; } Exp_val.prototype.validate= function (fd){ if (!fd){ this .on_suc(); return true ; } if ( this .exps.test(fd)){ this .on_suc(); return true ; } else { this .on_error(); return false ; } } //远程验证器 function Remote_val(url,tip){ this .p_url=url; this .tips=tip; this .on_suc= null ; this .on_error= null ; } Remote_val.prototype.validate= function (fd){ var self= this ; $.post( this .p_url,{f:fd}, function (data){ if (data.rs){ self.on_suc(); return ; } else { self.on_error(); } }, "json" ); return false ; } //自定义函数验证器 function Man_val(tip,func){ this .tips=tip; this .val_func=func; this .on_suc= null ; this .on_error= null ; } Man_val.prototype.validate= function (fd){ if ( this .val_func(fd)){ this .on_suc(); } else { this .on_error(); } } |
最后我们用一个userform的类来做一个入口,在构造的时候传入Field对象的列表,并且将每一个控件的onblur事件绑定到validate的包装器上
1 2 3 4 5 6 7 8 9 10 11 12 13 | function UserForm(items){ this .f_item=items; //把字段验证对象数组复制给属性 for (idx=0;idx< this .f_item.length;idx++){ //循环数组 var fc= this .get_check( this .f_item[idx]); //获取封装后的回调事件 $( "#" + this .f_item[idx].field_id).blur(fc); //绑定到控件上 } } //绑定验证事件的处理器,为了避开循环对闭包的影响 UserForm.prototype.get_check= function (v){ return function (){ //返回包装了调用validate方法的事件 v.validate(); } } |
接下来需要定义一个方法来绑定提交按钮的onclick事件:
1 2 3 4 5 6 7 8 9 10 11 | //绑定提交事件到元件 UserForm.prototype.set_submit= function (bid,bind){ var self= this ; $( "#" +bid).click( function (){ if (self.validate()){ bind(); } } ); } |
这里提到了一个UserForm的validate方法,如下:
1 2 3 4 5 6 7 8 9 10 | //验证所有的字段 UserForm.prototype.validate= function (){ for (idx in this .f_item){ //循环每一个验证器 this .f_item[idx].validate(); //再检测一遍 if (! this .f_item[idx].checked){ return false ; //如果错误就返回失败,阻止提交 } } return true ; //一个都没错就返回成功执行提交 } |
最后用一个例子来看看怎么用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <html> <head> <meta http-equiv= "Content-Type" content= "text/html; charset=utf-8" /> <title>test</title> <script type= "text/javascript" src= "jquery-1.4.2.min.js" ></script> <script type= "text/javascript" src= "kernel.js" ></script> <script type= "text/javascript" > var form; $( function (){ var uf= new UserForm([ new Field({ fid: "f1" , val:[ new Len_val(1,5, "长度错误" ), new Exp_val(v_int, "不是数字" )], suc: function (text){ $( 't1' ).val( '' ); $( 't1' ).attr( 'class' , 'suc' ); }, err: function (text){ ( 't1' ).val(text); $( 't1' ).attr( 'class' , 'error' ); } }) ]); uf.set_submit( "bt" , function (form){ alert( "表单已经提交了" ); } ); } ); </script> <style> .suc { background-color: #00ff00;} .error { background-color: #ff0000;} </style> </head> <body> <input type= "text" id= "f1" name= "f1" /><span id= "t1" ></span><br/> <input type= "button" id= "bt" value= "提交" /> </body> </html> |
要注意的地方就是在循环中使用闭包会茶几,必须用一个方法来代理一下,呵呵 希望对初学js但是还不知道该做点什么怎么做的朋友能有所帮助