表单/验证表单——千万不要做一个只会拖控件、“照猫画虎”、copy/paste 程序员
在用 .NET 开发时,随便往页面上拖几个服务器端控件,在控件事件里写点代码处理一下——表单在哪?现在,便利的 IDE 环境越来越弱化“表单”的概念,这个概念的意义更多的是作为一个术语,写在书里,或是用于程序员之间的交流(当使用这个词时,彼此都知道对方在说什么)。当我毕业参加工作时,开发 Web 应用程序已经跟传统方式有很大区别,因此,一直无法体会“表单”。
“表单”概念对 Web 应用程序一直都重要——客户端向服务器端提交(以 get/post 方式)的数据。我是用 .NET 的,.NET 将控件区分成:HTML 控件(客户端控件)和服务器端控件。前者就是传统的、Web 应用程序最初的表单控件。但在 .NET 里,我们可以向任何控件添加 "runat=server" 属性,这样它就会被发送给服务器端。
若你认为,用就行了,想那么多干嘛!我见过太多这样的人,无论是我同学,还是网上,请教问题的态度真得很好——谦虚,礼貌。可抛出的问题,充分说明,他们的基础真的很差……事实说明,千万不要做一个只会拖控件、“照猫画虎”、copy/paste 程序员。
本文演示客户端验证表单,通过后发送到服务器端。
本文内容
- 表单验证
- 示例1:注册
- 示例2:购物
- .NET 表单验证
表单验证
示例1:注册
页面完成加载后,初始化页面(表单),添加提交表单前(onsubmit),对表单验证的事件。如果验证成功,则提交;否则,不提交。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" href="css/main.css" />
<script type="text/javascript">1:
2: window.onload = initForms;
3:
4: function initForms() {5: for (var i = 0; i < document.forms.length; i++) {6: document.forms[i].onsubmit = function() { return validForm(); }7: }
8: }
9:
10: function validForm() {11: var allGood = true;12: var allTags = document.getElementsByTagName("*");13:
14: for (var i = 0; i < allTags.length; i++) {15: if (!validTag(allTags[i])) {16: allGood = false;17: }
18: }
19: return allGood;20:
21: function validTag(thisTag) {22: var outClass = "";23: var allClasses = thisTag.className.split(" ");24:
25: for (var j = 0; j < allClasses.length; j++) {26: outClass += validBasedOnClass(allClasses[j]) + " ";27: }
28:
29: thisTag.className = outClass;
30:
31: if (outClass.indexOf("invalid") > -1) {32: invalidLabel(thisTag.parentNode);
33: thisTag.focus();
34: if (thisTag.nodeName == "INPUT") {35: thisTag.select();
36: }
37: return false;38: }
39: return true;40:
41: function validBasedOnClass(thisClass) {42: var classBack = "";43:
44: switch (thisClass) {45: case "":46: case "invalid":47: break;48: case "reqd":49: if (allGood && thisTag.value == "") classBack = "invalid ";50: classBack += thisClass;
51: break;52: default:53: if (allGood && !crossCheck(thisTag, thisClass)) classBack = "invalid ";54: classBack += thisClass;
55: }
56: return classBack;57: }
58:
59: function crossCheck(inTag, otherFieldID) {60: if (!document.getElementById(otherFieldID)) return false;61: return (inTag.value == document.getElementById(otherFieldID).value);62: }
63:
64: function invalidLabel(parentTag) {65: if (parentTag.nodeName == "LABEL") {66: parentTag.className += " invalid";67: }
68: }
69: }
70: }
71:
</script>
</head>
<body>
<form action="#">
<p>
<label for="userName">
名字:
<input type="text" size="30" id="userName" class="reqd" /></label></p>
<p>
<label for="passwd1">
密码:
<input type="password" id="passwd1" class="reqd" /></label></p>
<p>
<label for="passwd2">
确认密码:
<input type="password" id="passwd2" class="reqd passwd1" /></label></p>
<p>
<input type="submit" value="提交" /> <input type="reset" value="重置" /></p>
</form>
</body>
</html>
运行界面:
说明:
1,当某个字段不合法时,脚本会在 class 属性里添加 "invalid",在之后的处理中,如果一个标记的 class 属性里包含 "invalid",则改变第一个非法行的式样,并且焦点在那行的 input 标记;
2,页面里,每个标签的 class 属性用于指定表单验证时,使用的规则,用空格分隔,可以在函数 validBasedOnClass 看到。比如,class="reqd" 为应用“必填”规则;class="reqd password1" 为应用两个规则,除了必填外,该标记的值要与 ID为 "password1" 的值相等;
3,第 11 ~ 19 行,为验证的主函数。有意思的是,在这个主函数里,内嵌了几个子功能的函数,如 crossCheck、invalidLable等。在示例2,更清楚;
示例2:购物
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" href="css/main.css" />
<script type="text/javascript">1:
2: window.onload = initForms;
3:
4: // 初始化表单5: function initForms() {6: // 本页只有一个form7: for (var i = 0; i < document.forms.length; i++) {8: document.forms[i].onsubmit = function() { return validForm(); }9: }
10: document.getElementById("sunroof").onclick = function() {11: if (this.checked)12: document.getElementById("twoDoor").checked = true;13: else14: document.getElementById("twoDoor").checked = false;15: }
16: };
17: // 验证表单18: function validForm() {19: var allGood = true;20: var allTags = document.getElementsByTagName("*");21: for (var i = 0; i < allTags.length; i++) {22: if (!validTag(allTags[i])) {23: allGood = false;24: }
25: }
26: return allGood;27:
28: // 验证标记29: function validTag(thisTag) {30: var outClass = "";31: var allClasses = thisTag.className.split(" ");32:
33: for (var j = 0; j < allClasses.length; j++) {34: outClass += validBasedOnClass(allClasses[j]) + " ";35: }
36:
37: thisTag.className = outClass;
38:
39: if (outClass.indexOf("invalid") > -1) {40: invalidLabel(thisTag.parentNode);
41: thisTag.focus();
42: if (thisTag.nodeName == "INPUT") {43: thisTag.select();
44: }
45: return false;46: }
47: return true;48:
49: // 基于 class 验证50: function validBasedOnClass(thisClass) {51: var classBack = "";52:
53: switch (thisClass) {54: case "":55: case "invalid":56: break;57: case "reqd":58: if (allGood && thisTag.value == "") classBack = "invalid ";59: classBack += thisClass;
60: break;61: case "radio":62: if (allGood && !radioPicked(thisTag.name)) classBack = "invalid ";63: classBack += thisClass;
64: break;65: case "isNum":66: if (allGood && !isNum(thisTag.value)) classBack = "invalid ";67: classBack += thisClass;
68: break;69: case "isZip":70: if (allGood && !isZip(thisTag.value)) classBack = "invalid ";71: classBack += thisClass;
72: break;73: case "email":74: if (allGood && !validEmail(thisTag.value)) classBack = "invalid ";75: classBack += thisClass;
76: break;77: default:78: if (allGood && !crossCheck(thisTag, thisClass)) classBack = "invalid ";79: classBack += thisClass;
80: }
81: return classBack;82: }
83: // 用一个字段验证另一个字段84: function crossCheck(inTag, otherFieldID) {85: if (!document.getElementById(otherFieldID)) return false;86: return (inTag.value != "" || document.getElementById(otherFieldID).value != "");87: }
88: // 单选,必须选一个89: function radioPicked(radioName) {90: var radioSet = "";91:
92: for (var k = 0; k < document.forms.length; k++) {93: if (!radioSet) {94: radioSet = document.forms[k][radioName];
95: }
96: }
97: if (!radioSet) return false;98: for (k = 0; k < radioSet.length; k++) {99: if (radioSet[k].checked) {100: return true;101: }
102: }
103: return false;104: }
105: // 验证数字106: function isNum(passedVal) {107: var re = /\d/;108: return re.test(passedVal);109: }
110: // 验证邮编111: function isZip(inZip) {112: if (inZip == "") {113: return true;114: }
115: return (isNum(inZip));116: }
117: // 正则表达式验证Email118: function validEmail(email) {119: var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;120: return re.test(email);121: }
122: // 标识非法 Label123: function invalidLabel(parentTag) {124: if (parentTag.nodeName == "LABEL") {125: parentTag.className += " invalid";126: }
127: }
128: }
129: };
130:
</script>
</head>
<body>
<h2 style="text-align: center;">
挑选汽车</h2>
<form method="post" action="rec.aspx">
<p>
<label for="emailAddr">
Email地址:
<input id="emailAddr" type="text" size="30" class="reqd email" />
</label>
</p>
<hr />
<p>
<label for="color">
颜色:
<select id="color" class="reqd">
<option value="" selected="selected">选择颜色</option>
<option value="Red">红色</option>
<option value="Green">绿色</option>
<option value="Blue">蓝色</option>
</select>
</label>
</p>
<hr />
<p>
可选:
<label for="sunroof">
<input type="checkbox" id="sunroof" value="Yes" />Sunroof(只有2个门)</label>
<label for="pWindows">
<input type="checkbox" id="pWindows" value="Yes" />Power Windows</label>
</p>
<hr />
<p>
<label for="DoorCt">
门:
<input type="radio" id="twoDoor" name="DoorCt" value="twoDoor" class="radio" />2
门
<input type="radio" id="fourDoor" name="DoorCt" value="fourDoor" class="radio" />4
门
</label>
</p>
<hr />
<p>
<label for="zip">
填写邮编或选择你最近的供应商:<br />
邮编:
<input id="zip" type="text" size="5" maxlength="5" class="isZip dealerList" />
<select id="dealerList" size="4" class="zip">
<option value="California--Lemon Grove">California--Lemon Grove</option>
<option value="California--Lomita">California--Lomita</option>
<option value="California--Long Beach">California--Long Beach</option>
<option value="California--Los Alamitos">California--Los Alamitos</option>
<option value="California--Los Angeles">California--Los Angeles</option>
</select>
</label>
</p>
<hr />
<p>
<input type="submit" value="提交" /> <input type="reset" value="重置" /></p>
</form>
</body>
</html>
运行界面:
说明:
1,示例2 与示例1 差不多;
2,validForm 函数里内嵌了好几个功能性函数。这点比较有意思。
1: function validForm() {
2: var allGood = true;
3: var allTags = document.getElementsByTagName("*");
4: // 调用函数 validTag
5: // ......
6:
7: return allGood;
8:
9: function validTag(thisTag) {
10: var outClass = "";
11: var allClasses = thisTag.className.split(" ");
12: // 调用函数 validBasedOnClass
13: // ......
14: for (var j = 0; j < allClasses.length; j++) {
15: outClass += validBasedOnClass(allClasses[j]) + " ";
16: }
17:
18: thisTag.className = outClass;
19:
20: if (outClass.indexOf("invalid") > -1) {
21: // 调用函数 invalidLabel
22: // ......
23: return false;
24: }
25: return true;
26:
27: function validBasedOnClass(thisClass) {
28: // 使用 all Good 和 thisTag 变量
29: // 以及调用函数 crossCheck radioPicked isNum isZip validEmail
30: // ......
31: }
32:
33: function crossCheck(inTag, otherFieldID) {
34: // ......
35: }
36:
37: function radioPicked(radioName) {
38: // ......
39: }
40:
41: function isNum(passedVal) {
42: // ......
43: }
44:
45: function isZip(inZip) {
46: // ......
47: }
48: function validEmail(email) {
49: // ......
50: }
51: // 标识非法 Label
52: function invalidLabel(parentTag) {
53: // ......
54: }
55: }
在上面的结构,内嵌函数使用了它外层的变量。比如,定义在 validForm 里的 allGood 变量,在 validBasedOnClass 里使用了。这是 JavaScript 的一个特色,可以缓存数据,你可以查匿名函数的相关资料。
.NET 表单验证
上面表单验证,比如验证Email 地址,若在 .NET 里,可以用如下简单方式:
<asp:TextBox ID="emailAddr" runat="server"></asp:TextBox><br />
<asp:RegularExpressionValidator ValidationExpression="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$"
ID="RegularExpressionValidator1" ControlToValidate="emailAddr" runat="server"
ErrorMessage="Email 地址非法"></asp:RegularExpressionValidator>
运行界面:
本文版权归作者和博客园,欢迎转载。若要转载,请在文章注明原文出处。 |