當開發的鐘擺愈往 client 端靠近時,server 端要負責的事情也漸漸地搬到 client 端,表單驗證就是一例。
使用者輸入資料送出,經過 server 進到資料庫,這過程至少有三個關卡可以做資料驗證。
從後面看回來,首先,資料庫是一定要檔的,不然就會收到一 堆很容易讓程式(與工程師)爆炸的資料;接下來是 server,也就是在我接觸到 Validation 之前偏好的方式,為此也做了一些功課,就是 Spring MVC 提供的驗證功能;最後就是 client 端,也就是在 Web 用 Javascript 做驗證,在我剛寫 Web 的時候,那時候 Struts 1.x 正紅,也提供了自動產生 Javascript 驗證碼的功能,後來琵琶別抱,掉入 Spring 之後,就漸漸遺忘 client 端驗證了,在遇到 jQuery Validataion 之前,只用來做一些簡單的驗證,因為要 hard code 所以挺累的,就一直不是我喜愛的驗證主角。
Javascript 做表單欄位驗證並不是多艱深的學問,自己寫一套也不是不可能的事,只是想到跨瀏覽器、跨平台這些鳥問題,還是用別人寫好的算了,尤其是這個簡單好用的 Validation。
Validation 誕生的相當早,遠在 2006 年,一個 plugin 光是可以活這麼久,一定有過人之處(不禁讓我想起,在遇到 jQuery 之前,我是多麼喜歡 Prototype...),目前版本是 1.11.1。
Validation 第一個吸引我的特色,就是相當簡單用,簡單到像 CKEditor 一樣(不知道是誰抄誰,也許這種方式已經變成主流了),只要為 Html Dom 物件加上指定的 class,然後呼叫一行 Javascript code 就可以了。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF8"> <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script> <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.js"></script> <title>Validation</title> <script type="text/javascript"> $(function(){ $('#theForm').validate(); }); </script> </head> <body> <form id="theForm"> <span>Title: </span><input type="text" name="title" class="required"/><br/> <input type="submit"/> </form> </body> </html>在必填的欄位加上 required 這個 class,然後呼叫 $(...).validate() 就大功告成了,其他內建的驗證規則請參考 API 介紹的最後一部分。
當然,class 不是唯一的用法,還有其他方式,像是設定在 Dom 物件的 attribute,或者用 jQuery metadata plugin(沒聽過,有空再研究),或者透過 API 的 rules 設定,可以從 validate(options) 設定,或者 rules(...) 設定,官方建議使用 class 或者 attribute 方式。
還有,自訂錯誤訊息是一定要的,官方提供的幾十種語系(截至目前版本 1.11.1 有 45 個語系檔),只要再加上下面這個連結,就可以看到中文了。
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/localization/messages_zh_TW.js "></script>看到這邊,我就決定在以後每個案子都使用 Validation 了,真是相見恨晚!
這種以 class 作為識別的作法,還帶來其他好處,例如我們常會在必填欄位的欄位名稱前面加上 * 號,但有時候總是會忘記,導致該加沒加,或不該加的卻忘記拿掉,這一切都是起因於資料不同步,資料會不同步就是放在多個地方造成的,如果只加了 class,然後透過下面的小程式,就可以少掉好幾個 bug 了。
$('#theForm .required').each(function(){ $(this).siblings('span').first().prepend('*'); });上面範例可以再進階,就是檢查不是 required 的,卻加上 * 的要拿掉。
另外 Validation 還有一些細節對使用者體驗是很重要的:
- 表單驗證後,除了在有錯的欄位顯示相關訊息外,還會自動 focus 在第一個錯誤的欄位;但是當使用者在送出表單時,如果游標是停在某個錯誤的欄位上,那表單驗證後,則是 focus 在送出表單前的欄位,而不是第一個錯誤的欄位,可以透過 focusInvalid 關閉這貼心的設計。
- 表單驗證顯示錯誤訊息後,當使用者更正了錯誤,不用重新呼叫 validate(),錯誤訊息會自動消失,但是這個同步驗證的功能只有在送出表單驗證後才會啟動,意思就是在第一次送出表單前,是不會有這個同步驗證的功能。
- 同步驗證的功能涵蓋「未出錯」的欄位,譬如說一開始有填值的欄位,經過驗證沒有出錯,但是後來手賤又把它清空,或者非必填的 Email 或者 Url,一開始留空沒出錯,驗證後輸入格式不符的值,這些狀況都會觸發同步驗證。
API 介紹
validate(options)
- Validation 第一個也是最重要的一個 function,可以傳入一堆設定值。
- debug: false
- 真是好用的設定,以前都沒想過。
- 在開發 Javascript 時,經常需要 submit 去測試,但又不想真的 submit 出去,以前就是在最後要 submit 時,加上 alert 去表示 Javascript 無誤,然後將 sumit() 註解掉,這樣是可行,只是有時候,開發完忘記復原,就被 PM 記一個 bug 了。
- 現在只要加上 defaule: true 就有一樣的效果了,只是開發完還是得記得拿掉才行。
- 啟動 debug 模式時,有些錯誤訊息只會出現在 Console 裡,這就要安裝 Firebug 了。
- submitHandler: callback
- 驗證無誤後,送出表單的 callback function,預設為表單本身的 theForm.submit()。
- 指定 submitHandler 的可能有:
- 在送出本單前,額外做一些處理。
$("#theform").validate({ submitHandler: function(form) { // 最後的處理... // 千萬不能用 $(form).submit() form.submit(); } });
- 改用 ajax 送出表單。
$("#theform").validate({ submitHandler: function(form) { $(form).ajaxSubmit(); // ajaxSubmit 由 jQuery Form plugin 提供 } });
- 傳入 submitHandler 的參數只有一個,就是 form 物件,是 DOM 物件,不是 jQuery 物件,差別在於,若是在 submitHandler 呼叫 $(form).submit(),會再次觸發 Validation 的驗證,因此導致無窮迴圈,所以要在 submitHandler 裡送出表單的話,一定要用 form.submit()。
- invalidHandler: callback
- 驗證有誤後,提供以不同的方式顯示錯誤訊息。
- 傳入兩個參數,event 物件與 validator 物件。
$('#theForm').validate({ invalidHandler: function(event, validator) { var errors = validator.numberOfInvalids(); if (errors) { $('#errorDiv').html('You have ' + errors + ' errors!').show(); // alert('You have ' + errors + ' errors!'); } else { $('#errorDiv').html('').hide(); } } });
- ignore: selector
- 用來指定不需要做驗證的欄位,底層使用 jQuery 的 not(),所以接受的用法同 jQuery 的 not()。
- 預設為 :hidden,看不到的欄位當然不需要驗證,最常用的就是隱藏的物件 id。
- jQuery 的 :hidden 指的是:
- display: none
- type="hidden"
- 長寬為零
- 外層物件為 hidden,也就是外層物件符合上面三個定義。
- 有些不需要指定就自動剔除的欄位有 submit button、reset button 與 disable 的欄位。
- rules: key-value pairs
- 不同於在 DOM 物件上使用 class 指定驗證規則,也可以使用 rules 集中做設定。
- key 就是 DOM 物件的 name 屬性,可以像是 text 或 password 這種單一物件,或者是 checkbox 或 radio 這種群組物件。
- 但是當 name 有中括號或者是點這類特殊字元時,就得用雙引號包起來。
- value 可以是:
- 字串,例如 required 或 email。
- 物件。
- function。
$("#theform").validate({ rules: { name: "required", email: { required: true, email: true } "user[email]": "email", "user.address.street": "required", contact: { required: true, email: { depends: function(element) { return $("#contactform_email:checked") } } } } });
- 自訂錯誤訊息。
- Validation 的錯誤訊息來源有三,依照優先順序如下:
- messages 設定。
- DOM 物件的 title 屬性。
- 預設訊息,也就是 messages_zh_TW.js 的設定。
- key 就是 DOM 物件的 name 屬性。
- value 可以是:
- 字串。
- 物件,用於一個 DOM 物件有多個驗證 rule 時。
- function,傳入兩個參數,rule 的值與 DOM 物件。
$('#theForm').validate({ messages: { title: '哎喲' } messages: { title: { required: '哎喲', minlength: '嘖' } } messages: { title: { required: '哎喲', minlength: function(len, element){ return '不可少於 ' + len +' 個字元'; } } } messages: { title: { required: '哎喲', minlength: jQuery.format('不可少於 {0} 個字元!') } } });
- 前面提過,送出表單驗證失敗後,游標會停在送出前的位置,如果沒有就停在第一個錯誤的欄位上。
- 若不想要自動 focus,將 focusInvalid 設為 false 即可。
- 驗證後,Validation 會為驗證失敗的 DOM 物件加上 errorClass,並顯示錯誤訊息。
- 若將 focusCleanup 設為 true,在驗證失敗後,focus 到失敗的欄位時,Validation 會將 errorClass 移除並隱藏錯誤訊息,但是一旦 blur,若錯誤仍在,那麼 errorClass 會再加回來並顯示錯誤訊息。
- 不建議 focusCleanup: true 與 focusInvalid: true 同時使用,因為驗證失敗後被 focus 的欄位不會出現錯誤訊息。
- 驗證後,Validation 會為驗證失敗的 DOM 物件加上 errorClass,並以帶有 errorClass 的 Label 顯示錯誤訊息。
<span>Title: </span> <input type="text" name="title" class="required error" minlength="2"> <label for="title" class="error" style="display: inline;">必填</label><br>
- 驗證後,Validation 會為驗證成功的 DOM 物件加上 validClass。
<span>Title: </span> <input type="text" name="title" class="required valid" minlength="2">
- 驗證後,Validation 會為驗證失敗的 DOM 物件加上 errorClass,並以帶有 errorClass 的 Label 顯示錯誤訊息,這裡的 Label 是透過 errorElement 設定的。
- 因為 Label 有 for 屬性可以和 DOM 物件建立關聯,所以並不建議修改 errorElement 的預設值。
- 將錯誤集中顯示的三個設定。
- errorContainer 與錯誤內容無關,可用來顯示「錯誤發生了」之類的標題。
- errorLabelContainer: 則是指定用來集中顯示錯誤的容器所在。
- wrapper: 在集中顯示錯誤的容器中,如何排列個別的錯誤訊息。
$('#theForm').validate({ errorContainer: '#errorTitle', errorLabelContainer: '#errorDiv', wrapper: 'li' }); <div id="errorTitle" style="display: none; ">有錯誤發生!</div> <div id="errorDiv" style="display: none; "></div> <form id="theForm"> <span>Title: </span><input type="text" name="title" class="required" minlength="2"/><br/> <span>Age: </span><input type="text" name="age" class="required"/><br/> <input type="submit"/> </form>
- 自訂顯示錯誤訊息的 callback。
- 是在 Validator 的 context 下執行,也就是說在 showErrors 的 function 裡呼叫 this,就是呼叫 validator。
- 傳入兩個參數:
- errorMap:key 是 DOM 元件的 name 屬性,value 是錯誤訊息。
- errorList:由錯誤訊息 message 與 DOM 元件 element 所組成的物件 List。
$('#theForm').validate({ showErrors: function(errorMap, errorList) { // 可以用 this 呼叫 validator 的 methods alert('You got ' + this.numberOfInvalids() + ' errors!'); // 由 name/message 組成的 Map var html = 'errorMap: <br/>'; for (var name in errorMap) { html += name + ': ' + errorMap[name] + '<br/>'; } // 由 message 與 element 組成的物件 List html += 'errorList: <br/>'; for (var idx in errorList) { html += $(errorList[idx].element).attr('name') + ': ' + errorList[idx].message + '<br/>'; } $('#errorDiv').html(html).show(); // 當定義了 showErrors,Validation 就不會觸發預設的訊息顯示 // 但可以強制呼叫 defaultShowErrors() 來顯示 this.defaultShowErrors(); } });
- 自訂顯示錯誤訊息位置的 callback。
- 傳入兩個參數,兩個物件都是 jQuery 物件:
- 新增的 label 物件
- 發生錯誤的 DOM 物件
$('#theForm').validate({ errorPlacement: function(label, element) { if (element.attr('type') == 'radio' || element.attr('type') == 'checkbox') { element.parent().append(label); } else { label.insertAfter(element); } } });
- 用途類似 validClass。
- 傳入字串時,差別在於 validClass 將值加到 DOM 物件的 class,而 success 是將值加到 label 的 class 上。
- 傳入 funtion 時,可以得到 lable 的 jQuery 物件,常用來顯示正面的訊息。
$('#theForm').validate({ success: function(label) { label.addClass('success').text('Ok'); } });
- 如何標示驗證失敗的 DOM 物件,預設為將 errorClass 加到 DOM 物件上。
$('#theForm').validate({ highlight: function(element, errorClass, validClass) { // 預設的作法 $(element).addClass(errorClass).removeClass(validClass); } });
- 反轉 highlight,傳入參數相同。
- Validation 的錯誤訊息來源,僅次於 messages 設定的第二優先,為 DOM 物件的 title 屬性,但是未來版本將移除這功能。
- 預設為 false 是為了向前相容。
- groups
- onsubmit
- onfocusout
- onkeyup
- onclick
valid()
- 手動強制執行表單驗證。
- 必須呼叫過 validate(options) 之後,才能呼叫 valid()。
$('#theForm').validate(); $('#check').click(function(){ alert('Check: ' + $('#theForm').valid()); });
rules()
- 取得第一個物件的 Validation rule。
$('#info').click(function(){ var rules = $('#title').rules(); var rr = ''; for (var r in rules) { rr += r + ' - ' + rules[r] + '\r\n'; } alert(rr); // required - true minlenght - 2 });
rules(action, rules)
- action - 'add':新增驗證規則至所有符合的物件,並回傳第一個物件的驗證規則。
- action - 'remove':從第一個物件移除驗證規則,並回傳移除的驗證規則。
- 必須先呼叫 validate(options)。
- rules 用法同 validate(options) 裡的 rules,另外可以設定驗證訊息。
- 移除可以不用設定 rules,表示移除所有「動態」設定的 rules,也可以使用空白設定想要移除的 rules。
- 動態設定的 rules 指的是透過 rules(...) 或者 validate(options) 設定的 rules。
- 相對於透過 class 或者 attribute 設定的靜態 rules,是不能使用 rules('remove') 移除的。
- 新增:
$('#theForm').validate(); $("#title").rules("add", { required: true, minlength: 2, messages: { required: "標題不可以空白", minlength: jQuery.format("標題不可少於 {0} 個字") } });
- 移除:
$('#theForm').validate(); $("#title").rules("add", { required: true, minlength: 4, messages: { required: "標題不可以空白", minlength: jQuery.format("標題不可少於 {0} 個字") } }); var rules = $("#title").rules("remove"); // var rules = $("#title").rules("remove", 'required minlength');
removeAttrs(attributes)
- 移除第一個物件的指定屬性並回傳。
- 用於「略過」這類的特殊功能。
$('#theForm').validate(); $('#skipTitle').click(function(){ var rules = $("#title").removeAttrs("required"); $('#theForm').submit(); $("#title").attr(rules); });
自訂的 selector
- :blank
- 無值或都是空白。
- 實作類似 jQuery.trim(value).length == 0。
$('input:blank').css('background-color', 'red');
- :filled
- 有空白以外的值,為 :blank 的對照。
- 實作類似 jQuery.trim(value).length > 0。
$('input:filled').css('background-color', 'white');
- :unchecked
- 未勾選,為 :checked 的對照。
jQuery.validator.format(templateString, argument0orArray, argument1....)
- 參數化字串。
- 第一個參數為字串範本,使用大括號加上數字代表參數,數字從零算起。
- 第二個參數,若為字串,則取代 {0},若為陣列,則依序取代 {0}、{1}、、。
- 第三個參數取代 {1}...。
var template = '為什麼不早點發現 {0} {1}!!'; alert(jQuery.validator.format(template, 'jQuery', 'Validation'));
Validator object
- validate(options) 會回傳 Validator 物件,有以下的 API 可用。
- form()
- 手動執行表單驗證,顯示驗證結果,並回傳佈林值表示成功或失敗。
- 等同按下送出鍵。
var f = $('#theForm').validate().form(); alert(f);
- element(selector)
- 類似 form(),差別在於要傳入 selector 以驗證符合 selector 的物件,其餘相同。
var f = $('#theForm').validate().element('#title'); alert(f);
- resetForm()
- 重設表單,復原欄位的值(需要 jQuery Form plugin)、移除驗證失敗所加的 class 以及錯誤訊息。
<script type="text/javascript" src="http://malsup.github.com/jquery.form.js"></script> var v = $('#theForm').validate(); var f = v.element('#title'); alert(f); $('#resetForm').click(function(){ v.resetForm(); });
- showErrors(errors)
- 加入錯誤訊息並立即顯示。
var v = $('#theForm').validate(); v.showErrors({'title': '這是標題,一定要的!', 'age': '年齡不是秘密'});
- numberOfInvalids()
- 取得「目前」錯誤的欄位數。
- 常在 invalidHandler 裡使用。
Validator functions
- 類似 static method,透過 jQuery.validator.xxx() 呼叫。
- setDefaults(options)
- 設定 Validation 的預設值,接受的參數 options 同 validate(options),換句話說,透過 setDefaults(options) 設定後,即成為下一次呼叫 validate(options) 的預設值。
- setDefaults(options) 效力僅限於同一頁,當然也可以獨立成一隻 script 檔,由需要的網頁引入。
- 因此,setDefaults(options) 兩種用法:
- 同一頁呼叫多次 validate(options) 時,設定 setDefaults(options) 才有意義。
- 獨立成一隻 script 檔,由需要的網頁引入。
- addMethod(ruleName, callback[, message])
- 新增自訂的驗證規則,傳入三個參數:
- ruleName:規則名稱,必須是合格的 Javascript 變數名稱。
- callback:執行驗證的 function,回傳 true 表示驗證合格,傳入三個參數:
- value:待驗證的值。
- element:待驗證的物件。
- params:該驗證規則需要的參數,例如 min 或 max 的值。
- message:非必要,若未指定,則從 message.js 裡找。
jQuery.validator.addMethod('noA', function(value, element, params){ return this.optional(element) || value.indexOf('A') < 0; }, '不要輸入A!'); $('#theForm').validate();
- addClassRules
- 新增複合規則。
- 是 class rule,不是 attribute rule。
jQuery.validator.addClassRules('requiredAtLeast2Chars', { required: true, minlength: 2 }); jQuery.validator.addClassRules({ requiredAtLeast2Chars: { required: true, minlength: 2 }, requiredAtLeast3Chars: { required: true, minlength: 3 } }); $('#theForm').validate();
內建的驗證規則
- required()
- 必填欄位,適用於 text input、radio、checkbox 與 select。
- text input:不可無值或空白字元。
- radio/checkbox:至少勾選一項。
- select:要選取且選取的 value 不為空。
<form id="theForm"> <span>Title: </span> <input type="text" id="title" name="title"/><br/> <span>Sex: </span> <span> <input type="radio" id="sex_m" name="sex"/>Male <input type="radio" id="sex_f" name="sex"/>Female </span><br/> <span>Interests: </span> <span> <input type="checkbox" id="interests_m" name="interests"/>Movies <input type="checkbox" id="interests_b" name="interests"/>Book </span><br/> <span>Age: </span> <select name="age"> <option value=''>Choose...</option> <option value='10'>10</option> </select><br/> <input type="submit"/> </form> $('#theForm').validate({ rules: { title: 'required', sex: 'required', interests: 'required', age: 'required' }, errorPlacement: function(label, element) { if (element.attr('type') == 'radio' || element.attr('type') == 'checkbox') { element.parent().append(label); } else { label.insertAfter(element); } } });
- required(dependency-selector)
- 如果 dependency-selector 回傳一個以上的物件,那麼就是必填欄位,否則就是非必填。
$('#theForm').validate({ rules: { title: { required: '#title_required:checked' } } });
- 要小心 selector 的語法,不需要 $(...),只要括號裡的字串就可以了。
- 必填的邏輯同 required()。
- requried(dependency-callback)
- 如果 dependency-callback 回傳 true,那麼就是必填欄位,否則就是非必填。
- dependency-callback 傳入該物件。
$('#theForm').validate({ rules: { title: { required: function(element) { return $('#title_required:checked').length > 0; } } } });
- remote(string|options)
- 送出 ajax 到 server 來檢查值。
- string 是單一字串表示 URL,Validation 加上欄位名稱與輸入的值做參數。
$('#theForm').validate({ rules: { id: { required: true, remote: 'checkId.jsp' // server 得到 checkId.jsp?id=xxx } } });
- options 是一個設定物件,使用方式同 jQuery.ajax()。
$('#theForm').validate({ rules: { id: { required: true, remote: { url: 'checkId.jsp', // server 得到 checkId.jsp id=xxx&type=user&account=xxx type: 'post', data: { type: 'user', account: function(){ return $('#title').val(); } } } } } });
- minlength(length)
- maxlength(length)
- 適用於 text input、checkbox 與 multiple select。
- text input:看字串的長度。
- checkbox:看勾選的數量,與所勾選的值無關,也就是說勾選的值是空白也算一個。
- multiple select:看選擇的數量,與所選擇的值無關,也就是說選擇的值是空白也算一個。
$('#theForm').validate({ rules: { interests: { required: true, minlength: 2, maxlength: 3 } } });
- 有一個奇怪的現象,如果沒有設定 required,只有設定 minlength 與 maxlength 時,沒輸入/勾選/選擇時不會有驗證失敗!
- rangelength(range)
- 等於 minlength 加上 maxlength,適用對象也一樣。
$('#theForm').validate({ rules: { interests: { required: true, rangelength: [2, 3] } } });
- min(value)
- max(value)
- 僅適用於 text input,且只能輸入數值。
$('#theForm').validate({ rules: { age: { required: true, min: 18, max: 60 } } });
- 有一個奇怪的現象,如果沒有設定 required,只有設定 min 與 max 時,沒輸入時不會有驗證失敗!
- range(range)
- 等於 min 加上 max,適用對象也一樣。
$('#theForm').validate({ rules: { age: { required: true, range: [2, 3] } } });
- email()
- 僅適用於 text input,必須輸入正確的 email 格式。
$('#theForm').validate({ rules: { email: { required: true, email: true } } });
- url()
- 僅適用於 text input,必須輸入正確的 url 格式。
- 正確指的是以 http:// 或 https:// 開頭。
$('#theForm').validate({ rules: { url: { required: true, url: true } } });
- date()
- 僅適用於 text input,不準。
- 接受 2013/04/05 與 2013/2/30,不接受 2013/13/13。
- additional-methods.js 裡有其他 date 的驗證可用。
- dateISO()
- 僅適用於 text input,不準。
- 接受 2013/04/05 、 2013/2/30 與 2013/13/13。
- additional-methods.js 裡有其他 date 的驗證可用。
- number()
- 僅適用於 text input,必須輸入數字,接受小數。
$('#theForm').validate({ rules: { price: { required: true, number: true } } });
- digits()
- 僅適用於 text input,必須輸入正整數,不接受小數。
- 若需要負整數,請用 additional-methods.js 裡的 integer。
$('#theForm').validate({ rules: { age: { required: true, digits: true } } });
- creditcard()
- 僅適用於 text input,驗證信用卡號碼是否正確,無法驗證是否有效。
$('#theForm').validate({ rules: { creditcard: { required: true, creditcard: true } } });
- equalTo(other)
- 不用多說,就是用來確認密碼的。
$('#theForm').validate({ rules: { password: { required: true }, password_confirm: { equalTo: '#password' } } });
additional-methods.js 提供的驗證規則
- maxWords
- minWords
- rangeWords
- letterswithbasicpunc
- alphanumeric
- lettersonly
- nowhitespace
- ziprange
- zipcodeUS
- integer
- vinUS
- dateITA
- iban
- dateNL
- phoneNL
- mobileNL
- postalcodeNL
- bankaccountNL
- giroaccountNL
- bankorgiroaccountNL
- time
- time12h
- phoneUS
- phoneUK
- mobileUK
- phonesUK
- postcodeUK
- strippedminlength
- email2
- url2
- creditcardtypes
- ipv4
- ipv6
- pattern
- require_from_group
- skip_or_fill_minimum
- accept
- extension
---
---
---
沒有留言:
張貼留言