javascript改進表單

來源:互聯網
上載者:User

     昨天看了《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;width:300px;}fieldset {border:1px solid #ccc;margin-bottom:10px;}fieldset.login input {width:125px;}legend {font-weight:bold;font-size:1.1em;}label {display:block;width:60px;text-align:right;float:left;padding-right:10px;margin:5px 0px;}input {margin:5px 0;}input.text {padding:0 0 0 3px;width:172px;}input.submit {margin:15px 0 0 70px;}</style>

希望對js感興趣並且學習不久的園友能夠自己親自敲下代碼,體驗一下。

出來的效果就像這樣:

給出了樣式原型後,作者寫了一些驗證函式做引導,介紹些Regex匹配的用法。我們跳過這些直接進入重點,先來看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方法通過匹配Regex去掉字串首尾空格,很常用。對於下面一些其他欄位的判斷如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;      width: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,寫到這裡算是把問題解決完了,完整執行個體下載, 如果有什麼問題歡迎大家討論: )

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.