zoukankan      html  css  js  c++  java
  • javascript改进表单

         昨天看了《Pro Javascript Techniques》中的用js改进表单的那章,自己把代码敲了一遍,用firebug调试了半天,发现了书中的一些小问题,我会在下面复习的过程中提出,欢迎大家留言讨论 : )

         在表单方面,验证用户输入是否合法是个重头,我就直接拿书中的例子展开吧,先给出语义化的XHTML

    <body>
    		<form action="" method="post">
    			<fieldset class="login">
    				<legend>Login Information</legend>
    				<label for="username" class="hover">Username</label>
    				<input type="text" id="username" class="required text"/><br/>
    				
    				<label for="password" class="hover">Password</label>
    			    <input type="password" id="password" class="required text"/>
    			</fieldset>
    			<fieldset>
    				<legend>Personal Information</legend>
    				<label for="name">Name</label>
    				<input type="text" id="name" class="required text"/><br/>
    				
    				<label for="email">Email</label>
    				<input type="text" id="email" class="required email text"/><br/>
    				
    				<label for="date">Date</label>
    				<input type="text" id="date" class="required date text"/><br/>
    				
    				<label for="url">Website</label>
    				<input type="text" id="url" class="url text" value="http://"/><br/>
    				
    				<label for="phone">Phone</label>
    				<input type="text" id="phone" class="phone text"/><br/>
    				
    				<label for="age">Over 13?</label>
    				<input type="checkbox" id="age" name="age" value="yes"/><br/>
    				
    				<input type="submit" value="Submit Form" class="submit"/>
    			</fieldset>
    		</form>
    	</body>
    

     在表单内,所有<input>元素都精细地分好类(比如,type为text的元素其class也为text,必填项的class为required为js的运用做个钩子),并与正确的label一起被包含在合适的fieldset内。并配上css使得更美观一些:

                             <style type="text/css">
    			form {
    				font-family:Arial;
    				font-size:14px;
    				300px;
    			}
    			fieldset {
    				border:1px solid #ccc;
    				margin-bottom:10px;
    			}
    			fieldset.login input {
    				125px;
    			}
    			legend {
    				font-weight:bold;
    				font-size:1.1em;
    			}
    			label {
    				display:block;
    				60px;
    				text-align:right;
    				float:left;
    				padding-right:10px;
    				margin:5px 0px;
    			}
    			input {
    				margin:5px 0;
    			}
    			input.text {
    				padding:0 0 0 3px;
    				172px;
    			}
    			input.submit {
    				margin:15px 0 0 70px;
    			}
    		</style>
    

    希望对js感兴趣并且学习不久的园友能够自己亲自敲下代码,体验一下。

    出来的效果就像这样:

    给出了样式原型后,作者写了一些验证函数做引导,介绍些正则表达式匹配的用法。我们跳过这些直接进入重点,先来看js验证的规则集合,可以注意到所有测试都分别需要通用的名称和语义化的错误信息。下面给出本例中使用的验证规则集合:

    var errMsg = {
    	required:{
    	    msg:'This field is required.',
    	    test:function(obj) {
    	                  return trim(obj.value).length > 0 || trim(obj.value) != obj.defaultValue;
    	            }
    	},
    	email: {
    	    msg:'Not a valid email address.',
    	    test:function(obj) {
    		return trim(obj.value).length <= 0 || /^[a-z0-9_+.-]+\@([a-z0-9-]+\.)+[a-z0-9]{2,4}$/i.test(obj.value);	
    	    }
    	},
    	phone: {
    	    msg:'Not a valid phone number.',
    	    test:function(obj) {
    	              var m = /(\d{3}).*(\d{3}).*(\d{4})/.exec( obj.value );
                                  if(m) obj.value = "(" + m[1] + ") " + m[2] + "-" + m[3];
                                  return trim(obj.value).length <= 0 || m;	
    	    }	
    	},	
    	date: {
    	    msg:'Not a valid date.',
    	    test:function(obj) {
    	              return trim(obj.value).length <= 0 || /^\d{2}\/\d{2}\/\d{2,4}$/.test(obj.value);
    	    }
             },
    	url: {
    	    msg:'Not a valid URL.',
    	    test:function(obj) {
                                 return trim(obj.value).length <= 0 || obj.value == 'http://' || /^https?:\/\/([a-z0-9-]+\.)+[a-z0-9]{2,4}.*$/.test(obj.value);
    	           }
    	}
    }
    

     这里我已经对验证规则做了修改,原文中对于必填项(required)的验证:return obj.value.length > 0这个子条件有问题,如果只是输入了一连串空格而无实质性的内容,难道也让它验证通过吗?很明显不行,所以用trim函数来去掉空格在来判断才正确。

    function trim(value) {
    	return value.replace(/(^\s*)|(\s*$)/g,'');
    }
    

     这个函数用replace方法通过匹配正则表达式去掉字符串首尾空格,很常用。对于下面一些其他字段的判断如email,date等的判断也是这个问题,这里有个细节,像email这种需要特定格式验证的字段,得先看看用户输入了东西没,如果没有那也没必要判断,所以用trim(obj.value).length <= 0如果为true那么这个||运算就到此为止了,说明用户没输入什么东西,也就没必要判断格式了,反之如果为false,就说明有内容了,并且需要判断下。

            规则给出来了,接着我们该用这些规则判断,然后给出必要的提示信息。先介绍个知识点:所有<form>元素(在DOM中)都有一个被称为elements的属性,这个属性是包含表单所有字段的数组,使用这个数组就可以轻松遍历所有可能的字段,并检查错误。在我debug的过程中,发现不光用户输入相关的<input>元素在elements中,两个<fieldset>元素也在其中...下面给出具体的验证相关的函数。

    //验证表单所有字段的函数
    //form是个表单元素的引用
    function validateForm(form) {
           var valid = true;
           //遍历表单的所有字段元素
         //form.elements是表单所有字段的一个数组
           for(var i = 0; i < form.elements.length; i++) {
                       //先隐藏任何错误信息,以防不意的显示
                       hideErrors(from.elements[i]);
                       //检查字段是否包含正确的内容
                 //注:原书中条件上加了个取反运算符'!',看了下面的validateField就知道这里有问题
                       if ( validateField(form.elements[i]) )
                                  valid = false;
           }  
           return valid;   
    }
    
    //验证单个字段的内容
    function validateField(elem) {
          var errors = [];
          //遍历所有可能的验证技术
        for(var name in errMsg ) {
                //查看字段是否有错误类型指定的class
                var re = new RegExp('(^|\\s)' + name + '(\\s|$)');
                //检查元素是否带有该class并把它传递给验证函数
            if( re.test(elem.className) && !errMsg[name].test(elem) )
                       //如果没有通过验证,把错误信息增加到列表中
                 errors.push( errMsg[name].msg );
          }
          //如果存在错误信息,则显示出来
        if(errors.length)  showErrors( elem,  errors );
          return errors.length > 0; //>0说明有错误信息
    } 
    
    /*显示和隐藏相应字段的错误信息*/
    //隐藏当前正显示的任何错位信息
    function hideErrors(elem) {
         //获取当前字段的下一个元素
         var next = elem.nextSibling;
         //如果下一个元素是ul并有class为errors
         if(next && next.nodeName == 'UL' && next.className == 'errors')
                  //删掉它(这是我们'隐藏'的含义)
                  elem.parentNode.removeChild(next);
    }
    
    //显示表单内特定字段的错误信息
    function showErrors(elem, errors) {
         //获取当前字段的下一个元素
         var next = elem.nextSibling;
         //如果该字段不是我们指定的包含错误的容器
       if( next && ( next.nodeName != 'UL' || next.className != 'errors' ) ) {
              //我们得生成一个
           next = document.createElement('ul');
               next.className = 'errors';
               //并在DOM中把它插入到恰当的地方
           //从HTML中看,这里的elem.nextSibling指向的是'<br/>',
              //而<ul>本身默认的display为block
           elem.parentNode.insertBefore( next, elem.nextSibling );
         }
         //现在有了一个包含错误的容器引用,我们可以遍历所有的错误信息了
       for(var i = 0; i < errors.length; i++) {
              var li = document.createElement('li');
              li.innerHTML = errors[i];
              //并插入到DOM中
              next.appendChild( li );
        }
    }
    

    在配上错误信息的css样式:

    ul.errors {
          list-style:none;
          background:#ffcece;
          padding:3px;
          margin:3px 0 3px 70px;
          font-size:0.9em;
          165px;
    }
    

    先看下演示效果:

    不算很漂亮,但也还算不错...接下来我们就着手启动验证,对于何时验证,作者进行了一些讨论,如在提交时进行统一验证,或是填写时单独的对每个元素进行验证。我先给出作者的代码:

    /*自己写的事件辅助函数*/
    function addEvent(obj, event, eventHandler) {
           if(obj.addEventListener) {
                 obj.addEventListener(event, eventHandler, false);
           } else if ( obj.attachEvent ) {
                 obj.attachEvent(event, eventHandler);
           }
    }
    
    /*前面文章中提到过的stopDefault函数*/
    function stopDefault( e ) {
           if(e && e.preventDefault)  e.preventDefault();
           else window.event.returnValue = false;
           return false;
    }
    
    /**
     * 在表单提交时进行验证
     */
    //在表单提交的时候运行表单验证的函数
    function watchForm( form ) {
          //监听表单的提交事件
          addEvent( form, 'submit', function() {
                 return validateForm( form ); 
          });
    }
    
    addEvent(window, 'load', function() {
           var form = document.getElementsByTagName('form')[0];
           watchForm( form );
    });
    
    /**
     * 在字段改变时验证
     */
    function watchFields( form ) {
        //遍历表单内的所有字段
        for(var i = 0 ; i < form.elements.length; i++) {
               //并绑定'change'事件处理函数(它监听input元素的失焦)
               addEvent(form.elements[i], 'change', function() {
                         //一旦失去焦点,重验证该字段 
                         return validateField (this);
               });
       }
    } 
    
    addEvent(window, 'load', function() {
         var form = document.getElementsByTagName('form')[0];
         //监听表单所有字段变化    
         watchFields( form );
    });
    

     我一开始按照书上的代码进行测试,发现一些问题,比如用'change'来监听的话,我先让一个input.required获得焦点,然后我再让它失去焦点,就没有任何实时的反馈,因为input中的内容没有改变,还有一个问题,如果我email输错一次,它会给出一个错误信息,我再次输错,它就给出了两条重复的错误信息...更严重的是即使有错误,'submit'的绑定函数返回了false还是提交了,这里我自己也有点疑惑,后来用了上面的stopDefault函数,就有想要的效果了,个人认为应该把单个input元素的实时验证和表单提交整体验证结合起来,下面给出我修改后的代码:

    /*修改后的watchFields*/
    function watchFields(form) {
         for(var i = 0; i < form.elements.length; i++) {
                addEvent(form.elements[i], 'blur', function() {
                        //先去掉原先的错误信息,防止出现重复错误信息
                        hideErrors(this);        //add by myself
                        return validateField(this);
                });
         }
    }
    
    /*修改后的watchForm*/
    function watchForm(form) {
          addEvent(form, 'submit', function(e) {
                 //如果验证没有通过,阻止提交事件
                 if ( !validateForm( form ) )  stopDefault(e);
          });
    }
    
    addEvent(window, 'load', function() {
          var form = document.getElementsByTagName('form')[0];
          //监听每个input元素的blur事件
          watchFields(form);
          //监听form的提交事件
          watchForm(form);
    });
    

     ok,写到这里算是把问题解决完了,完整实例下载, 如果有什么问题欢迎大家讨论: )

  • 相关阅读:
    在Power BI报表和仪表板中显示刷新日期时间
    微软Power BI 每月功能更新系列——12月Power BI 新功能学习
    在Microsoft Power BI中创建地图的10种方法
    您应该将报表从Excel转换为Power BI的8个原因
    OBS录制全屏游戏的方法(超好录屏)
    关于Adobe Premiere Pro视音频不同步的解决方法
    Npcap:Nmap项目里一个为Windows而生的嗅探库 Npcap: Nmap Project's packet sniffing library for Windows
    关于被malloc分配内存的指针
    VS2017 高级使用方法
    Npcap环境配置(Winpcap后继者) pcap的一种
  • 原文地址:https://www.cnblogs.com/AndyWithPassion/p/1767352.html
Copyright © 2011-2022 走看看