转载:http://www.cnblogs.com/sicd/p/4613628.html
一、前言
最近在开发一个新项目,需要做登陆等一系列的表单提交页面。在经过“缜密”的讨论后,我们决定 不用外部流行的框架,如bootstrap,由于我负责的模块
仅仅是其中的一部分,因此少数服从多数,无奈只能抛弃bootstrap等提供的布局,样式以及验证等一些列如此方便的组件,(他们拒绝使用的原因也令人发省)。
那么问题就来了。
二、设计理念
我们都知道,在抛开外部框架,仅仅用JS+css+html 去开发一个页面,是很复杂的,尤其是在没有美工,前台的情况下。其实bootstrap 在一定程度上 为公司节省了 美工 前台的开支...
废话少说,1天搞定了页面之后,开始为表单添加JS验证。用原生JS写验证是一件很吃力的事情,就算把各种验证方式,正则表达式,为空判断,长度判断,复杂各类字符组合判断等,集中到一个文件当中,依然可能无法排除每个页面中各种的条件语句,万一一个表单 N个input呢?
而jquery validate组件 等 是怎样实现的呢,其实现实中表示,只要会用就行了 不用了解其工作原理。就像你只要会开汽车 就足够了,不用去了解车是怎么实现的,那么当你有一天会开飞机,那么你就登上了人生的“巅峰”。
三、自定义验证器
废话不多说 直接上代码
1 /**
2 * Created by sicd 2015-5-29.
3 */
4 $(function(){
5 });
6
7 var suc_img='<i class="error-img"></i>';
8 var err_tag='<span class="error-msg" id="error-msg" style="display: block;"></span>';
9
10
11
12 function isIP(strIP) {
13 if (isNull(strIP)) return false;
14 var re=/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/g //匹配IP地址的正则表达式
15 if(re.test(strIP))
16 {
17 if( RegExp.$1 <256 && RegExp.$2<256 && RegExp.$3<256 && RegExp.$4<256) return true;
18 }
19 return false;
20 }
21
22 /*
23 用途:检查输入字符串是否为空或者全部都是空格
24 输入:str
25 返回:
26 如果全是空返回true,否则返回false
27 */
28 function isNull( str ){
29 if ( str == "" ) return true;
30 var regu = "^[ ]+$";
31 var re = new RegExp(regu);
32 return re.test(str);
33 }
34
35
36 /*
37 用途:检查输入对象的值是否符合整数格式
38 输入:str 输入的字符串
39 返回:如果通过验证返回true,否则返回false
40
41 */
42 function isInteger( str ){
43 var regu = /^[-]{0,1}[0-9]{1,}$/;
44 return regu.test(str);
45 }
46
47 /*
48 用途:检查输入手机号码是否正确
49 输入:
50 s:字符串
51 返回:
52 如果通过验证返回true,否则返回false
53
54 */
55 function checkMobile( s ){
56 var regu =/^[1][3][0-9]{9}$/;
57 var re = new RegExp(regu);
58 if (re.test(s)) {
59 return true;
60 }else{
61 return false;
62 }
63 }
64
65
66 /*
67 用途:检查输入字符串是否符合正整数格式
68 输入:
69 s:字符串
70 返回:
71 如果通过验证返回true,否则返回false
72
73 */
74 function isNumber( s ){
75 var regu = "^[0-9]+$";
76 var re = new RegExp(regu);
77 if (s.search(re) != -1) {
78 return true;
79 } else {
80 return false;
81 }
82 }
83
84 /*
85 用途:检查输入字符串是否是带小数的数字格式,可以是负数
86 输入:
87 s:字符串
88 返回:
89 如果通过验证返回true,否则返回false
90
91 */
92 function isDecimal( str ){
93 if(isInteger(str)) return true;
94 var re = /^[-]{0,1}(\d+)[\.]+(\d+)$/;
95 if (re.test(str)) {
96 if(RegExp.$1==0&&RegExp.$2==0) return false;
97 return true;
98 } else {
99 return false;
100 }
101 }
102
103 /*
104 用途:检查输入对象的值是否符合端口号格式
105 输入:str 输入的字符串
106 返回:如果通过验证返回true,否则返回false
107
108 */
109 function isPort( str ){
110 return (isNumber(str) && str<65536);
111 }
112
113 /*
114 用途:检查输入对象的值是否符合E-Mail格式
115 输入:str 输入的字符串
116 返回:如果通过验证返回true,否则返回false
117
118 */
119 function isEmail( str ){
120 var myReg = /^[-_A-Za-z0-9]+@([_A-Za-z0-9]+\.)+[A-Za-z0-9]{2,3}$/;
121 if(myReg.test(str)) return true;
122 return false;
123 }
124
125 /*
126 用途:检查输入字符串是否符合金额格式
127 格式定义为带小数的正数,小数点后最多三位
128 输入:
129 s:字符串
130 返回:
131 如果通过验证返回true,否则返回false
132
133 */
134 function isMoney( s ){
135 var regu = "^[0-9]+[\.][0-9]{0,3}$";
136 var re = new RegExp(regu);
137 if (re.test(s)) {
138 return true;
139 } else {
140 return false;
141 }
142 }
143 /*
144 用途:检查输入字符串是否只由英文字母和数字和下划线组成
145 输入:
146 s:字符串
147 返回:
148 如果通过验证返回true,否则返回false
149
150 */
151 function isNumberOr_Letter( s ){//判断是否是数字或字母
152
153 var regu = "^[0-9a-zA-Z\_]+$";
154 var re = new RegExp(regu);
155 if (re.test(s)) {
156 return true;
157 }else{
158 return false;
159 }
160 }
161 /*
162 用途:检查输入字符串是否只由英文字母和数字组成
163 输入:
164 s:字符串
165 返回:
166 如果通过验证返回true,否则返回false
167
168 */
169 function isNumberOrLetter( s ){//判断是否是数字或字母
170
171 var regu = "^[0-9a-zA-Z]+$";
172 var re = new RegExp(regu);
173 if (re.test(s)) {
174 return true;
175 }else{
176 return false;
177 }
178 }
179 /*
180 用途:检查输入字符串是否只由汉字、字母、数字组成
181 输入:
182 value:字符串
183 返回:
184 如果通过验证返回true,否则返回false
185
186 */
187 function isChinaOrNumbOrLett( s ){//判断是否是汉字、字母、数字组成
188
189 var regu = "^[0-9a-zA-Z\u4e00-\u9fa5]+$";
190 var re = new RegExp(regu);
191 if (re.test(s)) {
192 return true;
193 }else{
194 return false;
195 }
196 }
197
198 /*
199 用途:判断是否是日期
200 输入:date:日期;fmt:日期格式
201 返回:如果通过验证返回true,否则返回false
202 */
203 function isDate( date, fmt ) {
204 if (fmt==null) fmt="yyyyMMdd";
205 var yIndex = fmt.indexOf("yyyy");
206 if(yIndex==-1) return false;
207 var year = date.substring(yIndex,yIndex+4);
208 var mIndex = fmt.indexOf("MM");
209 if(mIndex==-1) return false;
210 var month = date.substring(mIndex,mIndex+2);
211 var dIndex = fmt.indexOf("dd");
212 if(dIndex==-1) return false;
213 var day = date.substring(dIndex,dIndex+2);
214 if(!isNumber(year)||year>"2100" || year< "1900") return false;
215 if(!isNumber(month)||month>"12" || month< "01") return false;
216 if(day>getMaxDay(year,month) || day< "01") return false;
217 return true;
218 }
219
220 function getMaxDay(year,month) {
221 if(month==4||month==6||month==9||month==11)
222 return "30";
223 if(month==2)
224 if(year%4==0&&year%100!=0 || year%400==0)
225 return "29";
226 else
227 return "28";
228 return "31";
229 }
230
231 /*
232 用途:字符1是否以字符串2结束
233 输入:str1:字符串;str2:被包含的字符串
234 返回:如果通过验证返回true,否则返回false
235
236 */
237 function isLastMatch(str1,str2)
238 {
239 var index = str1.lastIndexOf(str2);
240 if(str1.length==index+str2.length) return true;
241 return false;
242 }
243
244
245 /*
246 用途:字符1是否以字符串2开始
247 输入:str1:字符串;str2:被包含的字符串
248 返回:如果通过验证返回true,否则返回false
249
250 */
251 function isFirstMatch(str1,str2)
252 {
253 var index = str1.indexOf(str2);
254 if(index==0) return true;
255 return false;
256 }
257
258 /*
259 用途:字符1是包含字符串2
260 输入:str1:字符串;str2:被包含的字符串
261 返回:如果通过验证返回true,否则返回false
262
263 */
264 function isMatch(str1,str2)
265 {
266 var index = str1.indexOf(str2);
267 if(index==-1) return false;
268 return true;
269 }
270
271
272 /*
273 用途:检查输入的起止日期是否正确,规则为两个日期的格式正确,
274 且结束如期>=起始日期
275 输入:
276 startDate:起始日期,字符串
277 endDate:结束如期,字符串
278 返回:
279 如果通过验证返回true,否则返回false
280
281 */
282 function checkTwoDate( startDate,endDate ) {
283 if( !isDate(startDate) ) {
284 alert("起始日期不正确!");
285 return false;
286 } else if( !isDate(endDate) ) {
287 alert("终止日期不正确!");
288 return false;
289 } else if( startDate > endDate ) {
290 alert("起始日期不能大于终止日期!");
291 return false;
292 }
293 return true;
294 }
295
296 /*
297 用途:检查输入的Email信箱格式是否正确
298 输入:
299 strEmail:字符串
300 返回:
301 如果通过验证返回true,否则返回false
302
303 */
304 function checkEmail(strEmail) {
305 //var emailReg = /^[_a-z0-9]+@([_a-z0-9]+\.)+[a-z0-9]{2,3}$/;
306 var emailReg = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
307 if( emailReg.test(strEmail) ){
308 return true;
309 }else{
310 alert("您输入的Email地址格式不正确!");
311 return false;
312 }
313 }
314
315 /*
316 用途:检查输入的电话号码格式是否正确
317 输入:
318 strPhone:字符串
319 返回:
320 如果通过验证返回true,否则返回false
321
322 */
323 function checkPhone( strPhone ) {
324 var phoneRegWithArea = /^[0][1-9]{2,3}-[0-9]{5,10}$/;
325 var phoneRegNoArea = /^[1-9]{1}[0-9]{5,8}$/;
326 var prompt = "您输入的电话号码不正确!"
327 if( strPhone.length > 9 ) {
328 if( phoneRegWithArea.test(strPhone) ){
329 return true;
330 }else{
331 alert( prompt );
332 return false;
333 }
334 }else{
335 if( phoneRegNoArea.test( strPhone ) ){
336 return true;
337 }else{
338 alert( prompt );
339 return false;
340 }
341
342
343 }
344 }
345
346
347 /*
348 用途:检查复选框被选中的数目
349 输入:
350 checkboxID:字符串
351 返回:
352 返回该复选框中被选中的数目
353
354 */
355
356 function checkSelect( checkboxID ) {
357 var check = 0;
358 var i=0;
359 if( document.all(checkboxID).length > 0 ) {
360 for( i=0; i<document.all(checkboxID).length; i++ ) {
361 if( document.all(checkboxID).item( i ).checked ) {
362 check += 1;
363 }
364 }
365 }else{
366 if( document.all(checkboxID).checked )
367 check = 1;
368 }
369 return check;
370 }
371
372 /*
373 *
374 * 检查是否是64的倍数
375 */
376 function Is64Multi(num){
377 if(num % 64 == 0){
378 return true;
379 }else{
380 return false;
381 }
382 }
383
384 function getTotalBytes(varField) {
385 if(varField == null)
386 return -1;
387
388 var totalCount = 0;
389 for (i = 0; i< varField.value.length; i++) {
390 if (varField.value.charCodeAt(i) > 127)
391 totalCount += 2;
392 else
393 totalCount++ ;
394 }
395 return totalCount;
396 }
397
398 function getFirstSelectedValue( checkboxID ){
399 var value = null;
400 var i=0;
401 if( document.all(checkboxID).length > 0 ){
402 for( i=0; i<document.all(checkboxID).length; i++ ){
403 if( document.all(checkboxID).item( i ).checked ){
404 value = document.all(checkboxID).item(i).value;
405 break;
406 }
407 }
408 } else {
409 if( document.all(checkboxID).checked )
410 value = document.all(checkboxID).value;
411 }
412 return value;
413 }
414
415
416 function getFirstSelectedIndex( checkboxID ){
417 var value = -2;
418 var i=0;
419 if( document.all(checkboxID).length > 0 ){
420 for( i=0; i<document.all(checkboxID).length; i++ ) {
421 if( document.all(checkboxID).item( i ).checked ) {
422 value = i;
423 break;
424 }
425 }
426 } else {
427 if( document.all(checkboxID).checked )
428 value = -1;
429 }
430 return value;
431 }
432
433 function selectAll( checkboxID,status ){
434
435 if( document.all(checkboxID) == null)
436 return;
437
438 if( document.all(checkboxID).length > 0 ){
439 for( i=0; i<document.all(checkboxID).length; i++ ){
440
441 document.all(checkboxID).item( i ).checked = status;
442 }
443 } else {
444 document.all(checkboxID).checked = status;
445 }
446 }
447
448 function selectInverse( checkboxID ) {
449 if( document.all(checkboxID) == null)
450 return;
451
452 if( document.all(checkboxID).length > 0 ) {
453 for( i=0; i<document.all(checkboxID).length; i++ ) {
454 document.all(checkboxID).item( i ).checked = !document.all(checkboxID).item( i ).checked;
455 }
456 } else {
457 document.all(checkboxID).checked = !document.all(checkboxID).checked;
458 }
459 }
460
461 function checkDate( value ) {
462 if(value=='') return true;
463 if(value.length!=8 || !isNumber(value)) return false;
464 var year = value.substring(0,4);
465 if(year>"2100" || year< "1900")
466 return false;
467
468 var month = value.substring(4,6);
469 if(month>"12" || month< "01") return false;
470
471 var day = value.substring(6,8);
472 if(day>getMaxDay(year,month) || day< "01") return false;
473
474 return true;
475 }
476
477 /*
478 用途:检查输入的起止日期是否正确,规则为两个日期的格式正确或都为空
479 且结束日期>=起始日期
480 输入:
481 startDate:起始日期,字符串
482 endDate: 结束日期,字符串
483 返回:
484 如果通过验证返回true,否则返回false
485
486 */
487 function checkPeriod( startDate,endDate ) {
488 if( !checkDate(startDate) ) {
489 alert("起始日期不正确!");
490 return false;
491 } else if( !checkDate(endDate) ) {
492 alert("终止日期不正确!");
493 return false;
494 } else if( startDate > endDate ) {
495 alert("起始日期不能大于终止日期!");
496 return false;
497 }
498 return true;
499 }
500
501 /*
502 用途:检查证券代码是否正确
503 输入:
504 secCode:证券代码
505 返回:
506 如果通过验证返回true,否则返回false
507
508 */
509 function checkSecCode( secCode ) {
510 if( secCode.length !=6 ){
511 alert("证券代码长度应该为6位");
512 return false;
513 }
514
515 if(!isNumber( secCode ) ){
516 alert("证券代码只能包含数字");
517
518
519 return false;
520 }
521 return true;
522 }
523
524 /****************************************************
525 function:cTrim(sInputString,iType)
526 description:字符串去空格的函数
527 parameters:iType:1=去掉字符串左边的空格
528
529 2=去掉字符串左边的空格
530 0=去掉字符串左边和右边的空格
531 return value:去掉空格的字符串
532 ****************************************************/
533 function cTrim(sInputString,iType) {
534 var sTmpStr = ' ';
535 var i = -1;
536
537 if (iType == 0 || iType == 1) {
538 while (sTmpStr == ' ') {
539 ++i;
540 sTmpStr = sInputString.substr(i, 1);
541 }
542 sInputString = sInputString.substring(i);
543 }
544
545 if (iType == 0 || iType == 2) {
546 sTmpStr = ' ';
547 i = sInputString.length;
548 while (sTmpStr == ' ') {
549 --i;
550 sTmpStr = sInputString.substr(i, 1);
551 }
552 sInputString = sInputString.substring(0, i + 1);
553 }
554 return sInputString;
555 }
556 function checkLength (str, lessLen, moreLen) {
557 var strLen = this.length(str);
558 if (lessLen != "") {
559 if (strLen < lessLen)
560 return false;
561 }
562 }
563
564 /*描述对字段验证的类*/
565 function Field(params){
566 this.field_id=params.fid; //要验证的字段的ID
567 this.validators=params.val; //验证器对象数组
568 this.on_suc=params.suc; //当验证成功的时候执行的事件
569 this.on_error=params.err; //当验证失败的时候执行的事件
570 this.checked=false; //是否通过验证
571 }
572
573 /*扩展这个类,加入validate方法*/
574 Field.prototype.validate=function(){
575 //循环每一个验证器
576 for ( var item=0;item<this.validators.length;item++ ){
577 /*for(item in this.valid{ators)*/
578 //给验证器附加验证成功和验证失败的回调事件
579 this.set_callback(this.validators[item]);
580 //执行验证器上的Validate方法,验证是否符合规则
581 if(this.validators[item]){
582 if(!this.validators[item].validate(this.data())){
583 break; //一旦任意一个验证器失败就停止
584 }
585 }
586 }
587 };
588 //获取字段值的方法
589 Field.prototype.data=function(){
590 return document.getElementById(this.field_id).value;
591 };
592
593 //获取该字段的对象
594 Field.prototype.obj=function(){
595 return document.getElementById(this.field_id);
596 }
597 /*设置验证器回调函数的方法set_callback如下:*/
598 Field.prototype.set_callback=function(val){
599 var self=this; //换一个名字来存储this,不然函数的闭包中会覆盖这个名字
600 if(val){
601
602 val.on_suc=function(){ //验证成功执行的方法
603 self.checked=true; //将字段设置为验证成功
604 /*self.on_suc(val.tips); //执行验证成功的事件*/
605 //不执行回调
606 $(self.obj()).attr('class','validate-suc');
607 $(self.obj()).nextAll('.error-img').show();
608 $(self.obj()).nextAll('.error-msg').hide().text(val.tips);
609
610 };
611 val.on_error=function(){ //验证失败的时候执行的方法
612 self.checked=false; //字段设置为验证失败
613 /*self.on_error(val.tips);//执行验证失败的事件*/
614 //不执行回调
615 $(self.obj()).attr('class','validate-err');
616 $(self.obj()).nextAll('.error-img').hide();
617 $('.error-msg').hide();
618 $(self.obj()).nextAll('.error-msg').css('display','inline').text(val.tips);
619 }
620 }
621 };
622 /*验证器*/
623
624 //非空验证
625 function Null_val(tip){
626 this.tips = tip;
627 this.on_suc=null;
628 this.on_error=null;
629 }
630 Null_val.prototype.validate=function(fd){
631 if(isNull(fd)){
632 this.on_error();
633 return false;
634 }
635 this.on_suc();
636 return true;
637 }
638
639 //长度验证
640 function Len_val(min_l,max_l,tip){
641 this.min_v=min_l;
642 this.max_v=max_l;
643 this.tips=tip;
644 this.on_suc=null;
645 this.on_error=null;
646 }
647 Len_val.prototype.validate=function(fd){
648 if(fd.length<this.min_v||fd.length>this.max_v){
649 this.on_error();
650 return false;
651 }
652 this.on_suc();
653 return true;
654 }
655
656 //正整数数字验证
657 function PosiInteg_val(tip){
658 this.tips=tip;
659 this.on_suc=null;
660 this.on_error=null;
661 }
662 PosiInteg_val.prototype.validate = function(fd){
663 if(!isNumber(fd)){
664 this.on_error();
665 return false;
666 }
667 this.on_suc();
668 return true;
669 }
670
671 //是否是64的倍数
672 function Is64Multiple(tip){
673 this.tips=tip;
674 this.on_suc=null;
675 this.on_error=null;
676 }
677 Is64Multiple.prototype.validate = function(fd){
678 if(!Is64Multi(fd)){
679 this.on_error();
680 return false;
681 }
682 this.on_suc();
683 return true;
684 }
685
686 //select是否选择验证
687 Select_isSelected.prototype.validate=function(fd){
688 if(fd == -1){
689 this.on_error();
690 return false;
691 }
692 this.on_suc();
693 return true;
694 }
695 function Select_isSelected(tip){
696 this.tips=tip;
697 this.on_suc=null;
698 this.on_error=null;
699 }
700
701 //正则表达式验证器
702 function Exp_val(expresion,tip){
703 this.exps=expresion;
704 this.tips=tip;
705 this.on_suc=null;
706 this.on_error=null;
707 }
708 Exp_val.prototype.validate=function(fd){
709 if(!fd){
710 this.on_suc();
711 return true;
712 }
713 if(this.exps.test(fd)){
714 this.on_suc();
715 return true;
716 }else{
717 this.on_error();
718 return false;
719 }
720 }
721 //远程验证器
722 function Remote_val(url,tip){
723 this.p_url=url;
724 this.tips=tip;
725 this.on_suc=null;
726 this.on_error=null;
727 }
728 Remote_val.prototype.validate=function(fd){
729 var self=this;
730 $.post(this.p_url,{f:fd},
731 function(data){
732 if(data.rs){
733 self.on_suc();
734 return;
735 }else{
736 self.on_error();
737 }
738 },"json"
739 );
740 return false;
741 }
742 //自定义函数验证器
743 function Man_val(tip,func){
744 this.tips=tip;
745 this.val_func=func;
746 this.on_suc=null;
747 this.on_error=null;
748 }
749 Man_val.prototype.validate=function(fd){
750 if(this.val_func(fd)){
751 this.on_suc();
752 }else{
753 this.on_error();
754 }
755 }
756
757 function FieldForm(items){
758 this.f_item=items; //把字段验证对象数组复制给属性
759 for(idx=0;idx<this.f_item.length;idx++){ //循环数组
760 var fc=this.get_check(this.f_item[idx]); //获取封装后的回调事件
761 $("#"+this.f_item[idx].field_id).change(fc); //绑定到控件上
762 }
763 };
764 //绑定验证事件的处理器,为了避开循环对闭包的影响
765 FieldForm.prototype.get_check=function(v){
766 return function(){ //返回包装了调用validate方法的事件
767 v.validate();
768 }
769 }
770 /*绑定按钮click*/
771 FieldForm.prototype.set_submit=function(bid,bind){
772 var self=this;
773 $("#"+bid).click(
774 function(){
775 if(self.validate()){
776 bind();
777 }
778 }
779 );
780 }
781 /*这里提到了一个FieldForm的validate方法*/
782 FieldForm.prototype.validate=function(){
783 for(idx in this.f_item){ //循环每一个验证器
784 this.f_item[idx].validate(); //再检测一遍
785 if(!this.f_item[idx].checked){
786 return false; //如果错误就返回失败,阻止提交
787 }
788 }
789 return true; //一个都没错就返回成功执行提交
790 }
791
792 //绑定输入框foucs事件 检测
793 $(function(){
794 $('.theme-normal input').bind('click', function() {
795 if($(this).hasClass('validate-err')){
796 $('.error-msg').hide();
797 $(this).nextAll('.error-msg').css('display','inline');
798
799 }
800 });
801 });
原理就不多说了,或多或少都有注释,结构就是很简单,最上面一部分是验证方法集合,本人偷懒 直接写在一个文件里,中间部分就是验证器,关于事件触发,回调,定义等,
稍微花点时间就能够看懂。当然 本验证器 还有很多优化的地方,可以通过调整,改变成符合自己的专用验证器。
下面是调用,或者说是赋予标签 验证器:
var form;
$(function() {
//表单验证
var uf = new FieldForm([
new Field({fid : "captcha",val : [ new Null_val("验证码为必填项,请输入")]}),
new Field({fid : "username",val : [ new Null_val("用户名为必填项,请输入"),new Len_val(1,8,"用户名长度不能超过8位,请重新输入!")]}),
new Field({fid : "password",val : [ new Null_val("密码为必填项,请输入"),new Len_val(1,32,"密码长度不能超过32位,请重新输入!")]})
]);
uf.set_submit("subBtn", function(form) {
$('#subBtn').parents('form').submit();
});
});
效果
其中样式 是我自己写的,当input(包括下拉框) 值 发生变化 则进行验证(可以调整验证器,改变触发形式),提交触发验证,input获得焦点时
如果之前验证不通过,则弹出提示框。
四、总结
很简单,一下午就能看懂 并且调整自如,由于本人懒,没有展示CSS相关的代码文件,如有兴趣,可以吧这些东西提取出来或者做整合
做成组件,做成像日期控件那样的,方便移植和运用。