当你需要经常在服务器上校验数据时,在Web页面上的另加校验就有诸多好处。多数情况下,用户会被表单惹恼。而当用户填完表单时就校验数据,既有助于用户立即发现他们犯的错误,也能减少等待HTTP响应的时间、并减少服务器对错误表单输入的处理。本文介绍的,就是如何在表单所在页面对表单数据进行校验。
使用浏览器内置的表单校验
HTML5的一大特性,就是在不依赖脚本的情况下校验大多数的用户数据。该特性是通过使用表单元素上的校验特性来实现的。
元素校验不通过时
当一个元素校验不通过时,会发生两件事:
- 该元素会匹配到:invalid这个CSS伪类,它能让你给校验不过的元素提供特定的样式。类似地,校验通过的元素则会匹配:valid伪类。
- 当用户要发送数据时,浏览器会阻止这个表单、并展示一段错误信息。
所有的<input>
元素都能使用pattern特性来作校验。该特性采用一个区分大小写的正则表达式作为它的值。若元素的值非空、或者不匹配该特性所指定的正则表达式,该元素就会被认为是校验不通过的。
举个例子
1 2 3 4 5
| <form> <label for="choose">Would you prefer a banana or a cherry?</label> <input id="choose" name="i_like" pattern="banana|cherry"> <button>Submit</button> </form>
|
1 2 3 4 5 6 7
| input:invalid { border: 1px solid red; } input:valid { border: 1px solid green; }
|
在本例中,<input>
元素可以接受如下三种值:空字符串、字符串”banana”或”cherry”。
required特性
若某个元素需要在提交表单之前填一个值,那我们可以通过required特性来标记该元素。当该特性为true
时,所在的文本域不允许为空。
1 2 3 4 5
| <form> <label for="choose">Would you prefer a banana or cherry?</label> <input id="choose" name="i_like" pattern="banana|cherry" required> <button>Submit</button> </form>
|
1 2 3 4 5 6 7
| input:invalid { border: 1px solid red; } input:valid { border: 1px solid green; }
|
注意文本框相比上个例子有何不同:
注意:<input>
元素的type特性被设为email
或url
时,无需再使用pattern
特性来坐校验。指定email
类型时,文本框的值就得是个正确格式的email地址(同时指定mutiple特性时,还可以是一个逗号分隔的email地址列表)。指定url
类型的文本框则会自动匹配一个URL。
其他校验约束
所有接受用户输入的表单元素(<textarea>
, <select>
等)都支持required
特性,但要注意<textarea>
元素并不支持pattern
特性。
所有的文本输入框(<input>
或<textarea>
)都可以使用maxlength特性来约束文本大小。输入框的值若大于maxlength
指定的值,就会校验不通过。但是浏览器通常不会让用户输入超过文本框指定长度的文本。
对于数字输入框,min和max特性也提供了校验约束。若输入框的值小于min
的值或大于max
的值,输入框就会校验不通过。
来个完整点的例子:
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
| <form> <p> <fieldset> <legend>Title<abbr title="This field is mandatory">*</abbr></legend> <input type="radio" required name="title" id="r1" value="Mr" ><label for="r1">Mr. </label> <input type="radio" required name="title" id="r2" value="Ms"><label for="r2">Ms.</label> </fieldset> </p> <p> <label for="n1">How old are you?</label> 请注意支持pattern特性的浏览器会使用数字输入框时让该特性失效。 该特性在这里的用法仅是做个向后兼容而已。 --> <input type="number" min="12" max="120" step="1" id="n1" name="age" pattern="\d+"> </p> <p> <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory">*</abbr></label> <input type="text" id="t1" name="fruit" list="l1" required pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range"> <datalist id="l1"> <option>Banana</option> <option>Cherry</option> <option>Apple</option> <option>Strawberry</option> <option>Lemon</option> <option>Orange</option> </datalist> </p> <p> <label for="t2">What's your e-mail?</label> <input type="email" id="t2" name="email"> </p> <p> <label for="t3">Leave a short message</label> <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea> </p> <p> <button>Submit</button> </p> </form>
|
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
| body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; margin: 0; padding: 0 5px; } p > label { display: block; } input[type=text], input[type=email], input[type=number], textarea, fieldset { -webkit-appearance: none; width : 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } input:invalid { box-shadow: 0 0 5px 1px red; } input:focus:invalid { outline: none; }
|
定制错误信息
如我们上面看到的几个例子所示,每次用户要发送未通过校验的表单时,浏览器都会展示一段错误信息。该信息的展示方式取决于浏览器本身。
这些自动生成的信息有两大缺点:
- 没有标准的方法来用CSS改变其外观和体验。
- 它们还要依赖浏览器本地信息,这意味着你会在使用一种语言的页面中,遇到错误信息使用另一种语言展示的情况。
使用法语版本的浏览器浏览英文页面
浏览器 |
渲染效果 |
Firefox 17 (Windows 7) |
|
Chrome 22 (Windows 7) |
|
Opera 12.10 (Mac OSX) |
|
要定制这些信息的外观和文本,你必须使用JavaScript,没有只使用HTML和CSS的方法。
HTML5提供了约束验证API来检查和定制表单元素的状态。此外它也能改变错误信息的文本。来看个例子:
1 2 3 4 5
| <form> <label for="mail">I would like you to provide me an e-mail</label> <input type="email" id="mail" name="mail"> <button>Submit</button> </form>
|
在Javascript中,你可以调用setCustomValidity())方法:
1 2 3 4 5 6 7 8 9
| var email = document.getElementById("mail"); email.addEventListener("keyup", function (event) { if (email.validity.typeMismatch) { email.setCustomValidity("I expect an e-mail, darling!"); } else { email.setCustomValidity(""); } });
|
使用Javascript校验表单
若你想要控制错误信息的样式和效果,或者想处理那些不支持HTML5表单验证的浏览器,那你就只能使用Javascript了。
HTML5约束验证API
现在越来越多的浏览器开始支持约束验证API,而且也支持得越来越靠谱。该API囊括了各个表单元素上的一系列方法和属性。
约束验证API的属性
属性 |
描述 |
validationMessage |
可以是一段本地化的信息,用于描述表单控件所不满足的验证条件(如果有的话);当控件不支持任何约束验证(willValidae 的值为false )、或控件的值满足了约束条件时,也可以是一个空字符串 |
validity |
一个用于描述元素校验阶段的validityState对象 |
validity.customError |
元素触发了定制的错误时返回true ,否则返回false |
validity.patternMismatch |
元素的值不匹配提供的模板时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid伪类 |
validity.rangeOverflow |
元素的值大于提供的最大值时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid和:out-fo-range伪类 |
validity.rangeUnderflow |
元素的值小于提供的最小值时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid和:out-fo-range伪类 |
validity.stepMismatch |
元素的值不符合step 特性提供的规则时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid和:out-fo-range伪类 |
validity.tooLong |
元素的值的长度超过要求的最大长度时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid和:out-fo-range伪类 |
validity.typeMismatch |
元素的值不是正确的语法时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid伪类 |
validity.valid |
元素的值没有校验出的问题时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:valid,否则会匹配:invalid伪类 |
validity.valueMissing |
元素是必填项但没有值时返回true ,否则返回false 。当返回true 时,该元素会匹配CSS的:invalid伪类 |
willValidate |
元素在表单提交时会被校验则返回true ,否则返回false |
约束验证API的方法
方法 |
描述 |
checkValidity() |
元素的值没有校验出的问题时返回true ,否则返回false 。当校验不通过时,该方法还会在相应元素上触发invalid事件。 |
setCustomValidity(message) |
给元素添加一段定制的错误信息;若你手动设置了错误信息,则给元素会被当做校验未通过,并显示出特定的错误。这就能让你摆脱标准的约束验证API提供的错误信息,直接使用Javascript来建立自己的错误信息。该信息在报错时会展示给用户。 |
对于老旧浏览器,我们可以使用H5F之类的polyfill来弥补其对于约束验证API的不支持。由于这里你已经使用了Javascript,所以使用polyfill也并不会成为你设计或实现网站、Web应用时的额外负担。
使用约束验证API的例子
来看下如何使用该API来建立定制的错误信息,首先HTLM如下:
1 2 3 4 5 6 7 8 9 10
| <form novalidate> <p> <label for="mail"> <span>Please enter an email address:</span> <input type="email" id="mail" name="mail"> <span class="error" aria-live="polite"></span> </label> <p> <button>Submit</button> </form>
|
这个简单的表格用了novalidate特性来关闭浏览器的自动校验;这样我们的脚本就能控制整个校验过程了。但是这样做却不会禁用掉约束验证API、以及 :valid, :invalid, :in-range, :out-of-range 等一系列CSS伪类。这就意味着,虽然浏览器不会自动在表单发送前校验它的数据,但你仍可自己来做校验、并且按需给予表单样式。
aria-live特性用来确保我们定制的错误信息能展示给任何人,包括那些使用诸如屏幕阅读器等无障碍设备的人。
CSS
这段CSS用于装饰我们的表单、并让错误的输出看起来更吸引人些。
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
| body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; } p * { display: block; } input[type=email]{ -webkit-appearance: none; width: 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } input:invalid{ border-color: #900; background-color: #FDD; } input:focus:invalid { outline: none; } .error { width : 100%; padding: 0; font-size: 80%; color: white; background-color: #900; border-radius: 0 0 5px 5px; -moz-box-sizing: border-box; box-sizing: border-box; } .error.active { padding: 0.3em; }
|
JavaScript
下面的Javascript代码用来处理我们自定义的错误校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var form = document.getElementsByTagName('form')[0]; var email = document.getElementById('mail'); var error = document.querySelector('.error'); email.addEventListener("keyup", function (event) { if (email.validity.valid) { error.innerHTML = ""; error.className = "error"; } }, false); form.addEventListener("submit", function (event) { if (!email.validity.valid) { error.innerHTML = "I expect an e-mail, darling!"; error.className = "error active"; event.preventDefault(); } }, false);
|
下面是运行结果:
约束验证API给了我们处理表单校验的强大工具,并让我们能有力地控制其用户界面,这就远超过只用HTML和CSS所能做的事了。
不使用内置API来校验表单
有时,面对老旧浏览器和定制的组件,你不能(或者不想)使用约束验证API。但在这种情况下,你仍然可以使用Javascript来校验你的表单。表单的校验更多是个用户界面的问题、而非真正的数据校验。
要校验一个表单,你得先问你自己几个问题:
我需要进行什么类型的校验?
你得先决定如何校验数据,这取决于你:可以用字符串操作、类型转换、正则表达式等等。只要记住表单数据通常是一段文本,而且传到你的脚本里时也是一些字符串就好。
表单校验未通过时,我应该做什么?
这就是个UI的问题了。得靠你决定表单的行为:是不论是否通过都发送数据呢?还是高亮那些出现错误的字段?还是展示一些错误信息?
如何帮助用户用户纠正非法数据?
要减少用户的不满,很重要的是提供尽可能多的帮助信息,以指导用户纠正他们的输入。还应该提供些前置的输入建议、和清晰的错误信息,让用户知道该输入什么。
若你还想深入了解表单校验的UI要求,可以读读下面这些有用的文章:
未使用约束验证API的例子
为了更好地说明,我们接下来要重构上述的例子,让它能在老旧浏览器上运行:
1 2 3 4 5 6 7 8 9 10 11
| <form> <p> <label for="mail"> <span>Please enter an email address:</span> <input type="text" class="mail" id="mail" name="mail"> <span class="error" aria-live="polite"></span> </label> <p> <button type="submit">Submit</button> </form>
|
如你所见,HTML的和之前几乎一模一样;这里只移除了HTML5的部分。要注意的是ARIA是个并不依赖于HTML5的独立特性,所以我们仍会保留它。
CSS
类似地,CSS不需要做大规模修改;只要把:invalid
伪类转为真的类,并且不去使用IE6不支持的特性选择器即可。
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
| body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; } p * { display: block; } input.mail { -webkit-appearance: none; width: 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } input.invalid{ border-color: #900; background-color: #FDD; } input:focus.invalid { outline: none; } .error { width : 100%; padding: 0; font-size: 80%; color: white; background-color: #900; border-radius: 0 0 5px 5px; -moz-box-sizing: border-box; box-sizing: border-box; } .error.active { padding: 0.3em; }
|
JavaScript
Javascript代码的改变最大,为了兼容我们需要做更多的事情。
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
| var form = document.getElementsByTagName('form')[0]; var email = document.getElementById('mail'); var error = email; while ((error = error.nextSibling).nodeType != 1); var emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; function addEvent(element, event, callback) { var previousEventCallBack = element["on"+event]; element["on"+event] = function (e) { var output = callback(e); if (output === false) return false; if (typeof previousEventCallBack === 'function') { output = previousEventCallBack(e); if(output === false) return false; } } }; addEvent(window, "load", function () { var test = email.value.length === 0 || emailRegExp.test(email.value); email.className = test ? "valid" : "invalid"; }); addEvent(email, "keyup", function () { var test = email.value.length === 0 || emailRegExp.test(email.value); if (test) { email.className = "valid"; error.innerHTML = ""; error.className = "error"; } else { email.className = "invalid"; } }); addEvent(form, "submit", function () { var test = email.value.length === 0 || emailRegExp.test(email.value); if (!test) { email.className = "invalid"; error.innerHTML = "I expect an e-mail, darling!"; error.className = "error active"; return false; } else { email.className = "valid"; error.innerHTML = ""; error.className = "error"; } });
|
结果如下所示:
如你所见,自己建立一个校验系统并不是一件难事。难点就在于如何跨平台、并在你创建的任何表单上都能使用它。现在有许多库能执行表单校验,你应该毫不犹豫地使用他们。如下是几个例子:
远端校验
有些情况下使用远端校验还是挺有用的。在用户输入的数据得添加到应用服务器的数据存储时,这种校验是有必要的。一个相应的例子就是需要填用户名的注册表单。为避免用户名重复,更聪明的办法是发送一个AJAX请求来检查用户名的可用性,而不是让用户发送完数据、然后再返回一个带有错误的表单。
使用这种校验,需要注意以下几点:
- 因为需要公开暴露API和一些数据,所以得保证数据不是敏感的。
- 由于网络存在延迟,故得进行异步校验。这就得靠一些UI设计来保证校验不能正确执行时,用户操作不至于被阻塞。
结论
表单校验并不用靠复杂的Javascript,但它要求我们认真为用户着想,始终得帮用户纠正他们输入的数据。要做到这点,请确保:
- 展示明确的错误信息
- 约定好输入格式
- 指出错误出现的确切位置(特别是在那些巨大的表单中)