表单的基础知识
在HTML中,表单是由< form>元素来表示的,而在JS中,表单对应的则是HTMLFormElement类型。
HTMLFormElement类型继承了HTMLElement,因而与其他HTML元素具有相同的默认属性。不过HTMLFormElement也有它自己下列独有的属性和方法:
- action:接受请求的URL
- elements:表单中所有控件的集合
- length:表单中控件的数量
- method:要发送的HTTP请求类型,通常是get或者post
- name:表单的名称
- reset():将所有表单域重置为默认值
- submit():提交表单
取得< form>元素引用的方法有很多种。其中最常见的方式就是将它看成与其他元素一样,并为其添加id特性,然后再利用getElementById()来找到它。
其次,通过document.forms 可以取得页面中所有表单。在这个集合中,可以通过数值索引或name值来取得特定的表单。
var firstForm = document.forms[0];
var myForm = document.forms["forms2"];
另外,在较早的浏览器或者那些支持向后兼容的浏览器中,也会把每个设置了name特性的表单作为属性保存在document对象中,例如,通过document.form2就可以访问到name为form2的表单。但是不推荐这种方式,一是容易出错,而是将来的浏览器可能会不支持。
注意,可以同时为表单指定id和name属性,但它们的值不一定相同。
提交表单
用户单击提交按钮或图像按钮时就会提交表单。使用< input>或< button>都可以定义提交按钮,只要将其type特性的值设置为submit即可;而图像按钮则是通过将< input>的type特性值设置为“image”来定义的。
因此,只要单击以下代码生成的按钮,就可以提交表单:
<input type="submit" value="submit form">
<button type="submit" > submit form </button>
<input type="image" src="graphic.gif">
只要在表单中存在上面列出的任何一种按钮,那么在相应表单控件拥有焦点的情况下,按回车键就可以提交该表单。
如果表单中没有提交按钮,那么按回车键不会提交表单。
以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发submit事件。这样,我们就有机会验证表单数据,并据以决定是否允许表单提交。阻止这个事件的默认行为就可以取消表单提交。
EventHandler.addHandler(form,"submit",function(event){
event = EventUtil.getEvent(event);
//阻止默认事件
EventUtil.preventDefault(event);
})
这里调用了之前定义的跨浏览器的事件处理接口,使用preventDefault()方法阻止了表单提交。一般来说,在表单数据无效而不能发送给服务器时,可以使用这一技术。
在JS中,以编程方式调用submit()方法也可以提交表单。而且,这种方式无需表单包含提交按钮,任何时候都可以正常提交表单:
form.submit();
在调用submit()方法提交表单时,不会触发submit事件,因此要记得在调用此方法之前先验证表单数据。
提交表单时可能出现的最大问题,就是重复提交表单。解决这一问题的方法有两个:
- 在第一次提交表单之后就禁用提交按钮
- 利用onsubmit事件处理程序取消后续的表单提交操作。
重置表单
在用户单击重置按钮时,表单会被重置。使用type特性值为reset的< input>或< button>都可以创建重置按钮。
<input type="reset" value="reset form">
<button type="reset">reset form </button>
这两个按钮都可以用来重置表单。在重置表单时,所有表单字段都会恢复到页面刚加载完毕时的初始值。如果某个字段的初始值为空,就会恢复为空;而带有默认值的字段,也会恢复为默认值。
用户单击重置按钮重置表单时,会触发reset事件。利用这个机会,我们可以在必要时取消重置操作。
EventUtil.addHandler(form,"reset",function(event){
event = EventUtil.getEvent();
//阻止表单重置
EventTuil.preventDefault(event);
})
与提交表单一样,也可以通过JS来重置表单:
form.reset();
与调用submit()方法不同,调用reset()方法会像单击重置按钮一样触发reset事件。(submit()方法不会触发submit事件)。
表单字段
可以像访问页面中的其他元素一样,使用原生DOM方法访问表单元素。
- 每个表单都有elements属性,该属性是表单中所有表单元素的集合。
- elements集合是一个有序列表,其中包含着表单中的所有字段。
- 每个表单字段在elements集合中出现的顺序,与它们出现在标记中的顺序相同,可以按照位置和name特性来访问它们。
var field = form.elements[0];
var field2 = form.elements["textbox1"];
var fieldCount = form.elements.length;
如果有多个表单控件都在使用一个name(如单选按钮),那么就会返回以该name命名的一个NodeList。
共有的表单字段属性
除了< fieldset>元素之外,所有表单字段都拥有相同的一组属性。
由于< input>类型可以表示多种表单字段,因此有些属性只适用于某些字段,但还有一些属性是所有字段共有的。
表单字段共有的属性如下:
- disabled:布尔值,表示当前字段是否被禁用
- form:指向当前字段所属表单的指针;只读
- name:当前字段的名称
- readOnly:布尔值,表示当前字段是否只读
- tabIndex:表示当前字段的切换(tab)序号
- type:当前字段的类型
- value:当前字段将被提交给服务器的值。
除了form属性之外,可以通过JS动态修改其他任何属性。
var form = document.forms[0];
var field = form.elements[0];
field.value = "another value";
alert(field.form == form);
field.focus();
field.disabled = true;
field.type = "checkbox";
能够动态修改表单字段属性,意味着我们可以在任何时候,以任何方式来动态操作表单。
例如解决重复提交问题,最常见的解决方案,就是在第一次单击后就禁用提交按钮。只要侦听submit事件,并在该事件发生时禁用提交按钮即可。
EventHandler.addHandler(form,"submit",function(event){
event = EventUtil.getEvent(event);
var btn = target.elements["submit-btn"];
//禁用提交按钮
btn.disabled = true;
})
以上代码为表单的submit事件添加了一个事件处理函数。事件触发后,代码取得了提交按钮并将其disabled属性设置为true。
注意,不能通过onclick事件处理程序来实现这个功能,因为有的浏览器会在触发表单的submit事件之前触发click事件,而有的浏览器相反。对于先触发click事件的浏览器,利用onclick事件实现按钮失效,意味着会在提交发生之前就会禁用按钮,结果就是永远都不会提交表单。
因此,最好还是使用submit事件来禁用提交按钮。
不过,这种方式不适合表单中不包含提交按钮的情况;如前所述,只有在包含提交按钮的情况下,才有可能触发表单的submit事件。
除了< fieldset>方法之外,所有表单字段都有type属性。对于< input>元素,这个值等于HTML特性type的值。
此外,< input>和< button>元素的type属性是可以动态修改的,而< select>元素的type属性则是只读的。
共有的表单字段方法
每个表单字段都有两个方法:focus()和blur()。
focus()方法用于将浏览器的焦点设置到表单字段,即激活表单字段,使其可以响应键盘事件。使用focus()方法可以将用户的注意力吸引到页面中的某个部位。
要注意的是,如果要focus()的字段的type为hidden,或者使用CSS的display或visibility属性隐藏了该字段,那么使用focus()会导致错误。
HTML5为表单字段新增了一个autofocus属性。在支持这个属性的浏览器中,只要设置这个属性,不用JS就能自动把焦点移动到相应字段。
<input type="text" autofocus>
与focus()方法相对应的是blur()方法,它的作用是从元素中移走焦点。在调用blur()方法时,并不会把焦点转移到某个特定的元素上;仅仅是将焦点从调用这个方法的元素上面移走而已。
共有的表单字段事件
所有表单字段都支持下列3个事件:
- blur:当前字段失去焦点时触发
- change:对于< input>或< textarea>元素,在他们失去焦点且value值改变时触发;对于< select>元素,在其选项改变时触发。
- focus:当前字段获得焦点时触发
当用户改变了当前字段的焦点,或者调用了blur()和focus()方法时,都可以触发blur和focus事件。这两个事件在所有表单字段中都是相同的。
但是,change事件在不同表单控件中触发的次数会有所不同。
- 对于< input>和< textarea>元素,当他们从获得焦点到失去焦点且value值改变时,才会触发change事件。
- 对于< select>元素,只要用户选择了不同的选项,就会触发change事件。
注意,blur和change事件的发生顺序是不确定的。
文本框脚本
在HTML中,有两种方式来表现文本框:
- 使用< input>元素的单行文本框
- 使用< textarea>的多行文本框
要表现文本框,必须将< input>元素的type特性设置为“text”。
- 通过设置size特性,可以指定文本框中能够显示的字符数。
- 通过设置value特性,可以设置文本框的初始值
- maxlength特性,用于指定文本框可以接受的最大字符数。
如果要创建一个文本框,让它显示25个字符,但输入不能超过50个字符:
<input type="text" size="25" maxlength="50" value="initial value">
而< textarea>元素则始终会呈现一个多行文本框。要指定文本框的大小,可以使用rows和cols特性:
- rows:指定文本框的字符行数
- cols:指定文本框的字符列数
与< input>元素不同,< textarea>的初始值必须要放在< textarea>和< /textarea>中间。
<textarea rows="25" cols="5">initial value</textarea>
另一个与< input>的区别在于,不能在HTML中给< textarea>指定最大字符数。
无论这两种文本框在标记中有什么区别,但它们都会将用户输入的内容保存在value属性中。可以通过这个属性读取和设置文本框的值。
选择文本
上述两种文本框都支持select()方法,这个方法用于选择文本框中的所有文本。在调用select()方法时,大多数浏览器都会将焦点设置到文本框中。
这个方法不接受参数,可以在任何时候被调用。
var textbox = document.forms[0].elements["textbox1"];
textbox.select();
在文本框获得焦点时选择器所有文本,这是一种非常常见的做法,特别是在文本框包含默认值的时候,因为这样做可以让用户不必一个一个地删除文本。
EventUtil.addHandler(textbox,"focus",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
target.select();
})
将上面的代码应用到文本框之后,只要文本框获得焦点,就会选择其中所有的文本。
选择(select)事件
与select()方法对应的是select事件。在选择了文本框中的文本时,就会触发select事件。
在一些浏览器中,只有用户选择了文本(并且释放了鼠标)才会触发select事件。而在IE8及更早版本中,只要用户选择了一个字母(不必释放鼠标),就会触发select事件。
另外,在调用select()方法时也会触发select事件。
取得选择的文本
HTML5通过一些扩展方案让我们能指导用户选择了什么文本,即两个属性:
- selectionStart
- selectionEnd
这两个属性中保存的是基于0的数值,表示所选择文本的范围。
因此,要取得用户在文本框中选择的文本,可以使用如下代码:
function getSelectedText(textbox){
return textbox.value.substring(textbox.selectionStart,textbox.selectionEnd);
}
选择部分本文
除了select()方法之外,所有文本框都有一个setSelectionRange()方法。这个方法接收两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符的索引。
textbox.value = "hello world";
//选择所有文本
textbox.setSelectionRange(0,textbox.value.length); //"hello world"
//选择前三个字符
textbox.setSelectionRange(0,3);//"hel"
//选择第4到第6个字符
textbox.setSelectionRange(4,7);//"o w"
要看到选择的文本,必须在调用setSelectionRange()之前或之后立即将焦点设置到文本框。
过滤输入
我们经常会要求用户在文本框中输入特定的数据,或者特定格式的数据。综合运用事件和DOM手段,就可以将普通的文本框转换成能够理解用户输入数据的功能性控件。
屏蔽字符
有时候,我们要求用户输入的文本中包含或不包含某些字符。如前所述,响应向文本框中插入字符操作的是keypress事件。因此,可以通过阻止这个事件的默认行为来屏蔽此类字符。
在极端情况下,可以通过下列代码屏蔽所有按键操作:
EventUtil.addHandler(textbox,"keypress",function(event){
event = EventUtil.getEvent(event);
event.preventDefault(event);
})
运行以上代码后,由于所有按键操作都将被屏蔽,结果会导致文本框变成只读的。
如果只想屏蔽特定的字符,则需要检测keypress事件对应的字符编码,然后再决定如何响应。例如,下列代码只允许用户输入数值:
EventUtil.addHandler(textbox,"keypress",function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if(!/\d/.test(String.fromCharCode(event))){
event.preventDefault(event);
}
})
自动切换焦点
使用JS可以从多个方面增强表单字段的易用性。其中,最常见的一种方式就是在用户填写完当前字段时,自动将焦点切换到下一个字段。
通常,在自动切换焦点之前,必须知道用户已经输入了既定长度的数据。这样就可以在前一个文本框中的字符达到最大数量后,将焦点切换到下一个文本框。
HTML5约束验证API
HTML5为表单增加了一些功能,有了这些规则,就算JS被禁用了,浏览器也会自己根据标记中的规则进行验证,然后自己显示适当的错误信息。
只有在某些情况下表单字段才能进行自动验证。具体来说,就是要在HTML标记中为特定的字段制定一些约束,然后浏览器才会自动执行表单验证。
必填字段
第一种情况是在表单字段中指定了required属性。
<input type="text" name = "username" required>
任何标注有required的字段,在提交表单时都不能空着。
这个属性适用于< input>、< textarea>和< select>字段。
在JS中,可以通过对应的required属性,来检查某个表单字段是否为必填字段。
var isUsernameRequired = document.forms[0].elements["username"].required;
其他输入类型
HTML5为< input>元素的type属性又增加了几个值。这些新的类型不仅能够反映数据类型的信息,而且能提供一些默认的验证功能。
其中,“email”和“url”是两个得到支持最多的类型。
<input type="email" name ="email">
<input type="url" name ="homepage">
顾名思义,”email”类型要求输入的文本必须符合电子邮件地址的模式,而“url”类型要求输入的文本必须符合URL的模式。
数值范围
除了”email”和“url”,HTML5还定义了其他几个输入元素。
这几个元素都要求填写某种基于数字的值:
“number”,“range”,“datetime”,“datetime-local”,“date”,“month”,“week”,“time”。浏览器对这几个类型的支持情况并不好。
对所有这些数值类型的输入元素,可以指定min属性、max属性和step属性。例如,想让用户只能输入0到100的值,而且这个值必须是5的倍数,可以这样写代码:
<input type="number" min="0" max="100" step="5" name="count">
以上这些属性在js中都能通过对应的元素访问。此外,还有两个方法:stepUp()和stepDown(),都接受一个可选的参数:要在当前基础上加上或减去的数值。这两个方法还没有得到任何浏览器的支持。
input.stepUp(); //加1
input.stepDown(10); //减10
输入模式
HTML5为文本字段新增了pattern属性。这个属性的值是一个正则表达式,用于匹配文本框中的值。
例如,如果只想允许在文本字段中输入数值,可以像下面的代码一样应用约束:
<input type="text" pattern="\d+" name="count">
与其他输入类型类似,指定pattern也不能阻止用户输入无效的文本。这个模式应用给值,浏览器来判断值是有效还是无效。
检测有效性
使用checkValidity()方法可以检测表单中的某个字段是否有效。字段的值是否有效的判断依据是前面介绍过的那些约束。
禁用验证
通过设置novalidate属性,可以告诉表单不进行验证。
如果一个表单中有多个提交按钮,为了指定点击某个提交按钮不必验证表单,可以在相应的按钮上添加formnavalidate属性。
选择框脚本
选择框是通过< select> 和< option>元素创建的。这种类型的控件除了所有表单字段共有的属性和方法外,还有一些自己的属性和方法:
- add(newOption,relOption):向控件中relOption之前插入新< option>元素
- multiple:布尔值,表示是否允许多项选择;等价于HTML中的multiple特性。
- options:控件中所有option元素
- remove(index):删除给定位置的选项
- selectedIndex:基于0的选中项的索引,如果没有选中项则为-1 。 对于支持多选的空间,只保存选中项中第一项的索引。
- size:选择框中可见的行数,等价于HTML中的size特性。
选择框的value属性由当前选中项决定:
- 如果没有选中项,value保存空字符串
- 如果有一个选中项,而且该项的value特性已经在HTML中指定,那么value属性就等于已指定的特性;
- 如果有一个选中项,且该项的value特性在HTML中未指定,则选择框的属性等于该项的文本。
- 如果有多个选择项,value将根据前两条规则取第一个选中项的值。
而在DOM中,每个option元素也有自己的属性:
- index:当前选项在options中的索引
- label:当前选项的标签,等价于HTML中的label特性
- selected:布尔值,表示当前项是否被选中。将这个属性设为true可以选中当前项。
- text:选项的文本
- value:选项的值(等价于HTML中value特性的值)
其中大部分目的的属性,都是为了方便对选项数据的访问。虽然也可以用常规的DOM功能来访问这些信息,但效率还是比较低的。
//推荐
var text = selectBox.options[0].text;
var value= selectBox.options[0].value;
最后,对于选择框的change事件,只要选中了选项就会触发。
选择选项
可以通过selectedIndex来访问选中项。
可以通过设置某一项的selected属性为true来选中选项。
- 在允许多选的选择框中,设置选项的selected属性,不会取消对其他选中项的选择。
- 但是在单选框中,修改某个选项的selected属性则会取消对其他选项的选择。
- 将selected属性设置为false对单选选择框没有影响。
实际上,selected属性的主要作用是确定用户选择了选择框中的哪一项。要取得所有选择的项,可以循环遍历选项集合,然后测试每个选项的selected属性。
添加选项
可以使用js动态创建选项,并将它们添加到选择框中。
可以用DOM的createElement方法创建option元素并添加到选择框中。
而第二种方式,就是使用Option构造函数来创建新选项。
Option构造函数接受两个参数:text和value(可选)。
虽然这个构造函数会创建一个Object的实例,但是兼容DOM的浏览器会返回一个option元素。
var newOption = new Option("text","value_option");
selectBox.appendChild(newOption);
第三种添加新选项的方式是使用选择框的add()方法。这个方法接收两个参数:要添加的新选项和位于新选项之后的选项。
如果想在列表的最后添加一个选项,应该将第二个参数设置为null。
移除选项
- 可以使用DOM的removeChild()方法,为其传入要移除的选项。
- 可以使用选择框的remove()方法。这个方法接收一个参数,就是要移除选项的索引。
//移除第一个选项
selectBox.remove(0);
- 最后一种方式,将相应选项设置为null。
selectBox.options[0] = null;
要清除选择框中所有的项,需要迭代所有选项并逐个移除它们。
移动和重排选项
如果想要把第一个选择框中的某个选项移到第二个选择框中,直接使用DOM的appendChild()方法就可以,因为如果为appendChild传入一个文档中已有的元素,那么就会先从该元素的父节点中移除它,再把它添加到指定的位置。
移动选项和移除选项有一个共同之处,即会重置每一个选项的index属性。
重排选项也没有什么更好的方法,利用DOM的insertBefore(),可以将选择框中的某一项移动到特定位置。
appendChild()只适合将选项添加到选择框的最后。
//移动到它前面的选项之前
selectBox.insertBefore(optionToMove,selectBox.options[optionToMove.index-1]);
//移动到它后面的选项之后
selectBox.insertBefore(optionToMove,selectBox.options[optionToMove.index+2]);
表单序列化
在js中,可以利用表单字段的type属性,连同name和value属性一起实现对表单的序列化。
首先我们要搞清楚在表单提交期间,浏览器是怎样将数据发送给服务器的。
- 对表单字段的名称和值进行URL编码,使用&分隔
- 不发送禁用的表单字段
- 只发送勾选的复选框和单选按钮
- 不发送type为reset和button的按钮
- 多选选择框中的每个选中的值单独一个条目
- 在单击提交按钮提交表单的情况下,也会发送提交按钮;否则不发送提交按钮
在表单序列化过程中,一般不包含任何按钮字段,因为结果字符串很可能是通过其他方式提交的。除此之外的其他上述规则都应该遵循。
像jQuery提供了封装好的序列化方法:serialize()。