本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。
在HTML中,表单是用form元素来表示的,它与其他各种各样的表单输入元素,如input、select和button在客户端编程中有着重要的地位。
表单的作用是,通过表单元素收集用户的输入,再将这些输入数据提交给服务器,服务器处理接收到的数据并生成一个新的HTML页面显示在客户端;在以前,表单提交数据、服务器返回数据,此时客户端与服务端并没有其他交互行为,因此,数据提交交互性差、服务器负担重。
Javascript的最初的一个应用,就是分担服务器处理表单的任务,打破处处依赖服务器的局面;虽然现在Web和Javascript有了长足的发展,但Web表单的变化并不明显;特别是一些常见的形式,web表单并没有提供特别好的方案;最常见的操作,是使用Javascript增强了一些标准表单控件的默认行为。
表单及其控件都是HTML元素,可以使用标准的DOM技术来操作它们,另外表单已经脚本化了,也有专门的API,所以在表单编程中,最好使用表单API;
取得Form表单对象:
取得form元素引用的方式:最常用的方式就是将它看成与其他元素一样,使用getElementById()或getElementsByTagName()等标准的DOM技术;
varform=document.getElementById("myForm");varform=document.getElementsByTagName("form")[0];
通过document.forms集合可以取得页面中所有表单集合,在这个HTMLCollection集合中,可以通过数值索引或name或id值来取得特定的表单:
varfirstForm=document.forms[0];varmyForm=document.forms["myForm"];
较早的浏览器或者那些支持向后兼容的浏览器中,会把每个设置了name特性的表单作为属性保存在document对象中,如:
varform=document.myForm;
注:不推荐这种方式;注:可以同时为表单指定id和name属性,但它们可以设置不同的值;
表单属性和方法:
表单form在HTML中具有action、encoding、method和target等属性,在Javascript中,它是HTMLFormElement类型,继承了HTMLElement,因而与其他HTML元素一样具有相同的默认属性,但也具有自己独有的属性和方法,其中大部分的属性都与其在HTML中的属性相对应,这些属性可以控制表单是如何来提交数据并如何显示的,如:
acceptCharset:服务器能够处理的字符集,等价于HTML中的accept-charset特性;action:接受请求的URL,等价于HTML的action特性;elements:表单中所有控件的集合(HTMLCollection);enctype:请求的编码类型(方式),等价于HTML中的enctype特性;encoding:编码,等价于HTML的encoding特性,不般不需要;length:只读,表单中控件的数量;method:请求类型(方法),等价于HTML的method特性;name:表单的名称,等价于HTML的name特性;target:用于发送请求和接收响应的窗口名称,等价于HTML的targetset():方法,将所有表单域重置为默认值;submit():方法,提交表单;
以上的大部分属性是可写的;
scriptconsole.log(form.action);//demo.htmlform.action="/login";console.log(form.action);//login//获取表单信息functiongetFormInfo(){varinfo;varform=document.forms[0];info="form.elements:"+form.elements+"\n";info+="form.length:"+form.length+"\n";info+="form.name:"+form.name+"\n";info+="form.acceptCharset:"+form.acceptCharset+"\n";info+="form.action:"+form.action+"\n";info+="form.enctype:"+form.enctype+"\n";info+="form.encoding:"+form.encoding+"\n";info+="form.method:"+form.method+"\n";info+="form.target:"+form.target+"\n";form.elements["txt"].value=info;}functionsetFormInfo(form){form.name="yourForm"form.method="GET";form.action="/member";form.acceptCharset="gb";form.enctype="multipart/form-data";form.target="_blank";varpwd=document.cateElement("input");pwd.type="password";pwd.id="pwd";form.appendChild(pwd);}/scriptformname="myForm"id="myForm"action="/login"method="POST"pinputtype="text"id="username"name="username"//ppinputtype="button"value="表单信息"onclick="getFormInfo()"/inputtype="button"value="设置表单"onclick="setFormInfo(this.form)"//pptextaaid="txt"/textaa/p/form
也可以使用DOM方法动态创建表单,如:
varform=document.cateElement("form");document.body.appendChild(form);form.name="myform";form.action="/login";form.method="POST";//form.submit();//或者varbtn=document.cateElement("input");btn.type="submit";btn.value="提交";form.appendChild(btn);
Submit提交表单:使用input的type特性为”submit”或”image”就可以提交表单,或者button也可以提交表单;
inputtype="submit"value="提交"/inputtype="image"src="images/submit.png"/buttontype="submit"提交表单/button
以上的按钮,只要在任何表单元素拥有焦点的情况下,按回车就可以提交表单;如果表单里没有提交按钮,按回车键不会提交表单,但有个特例,如果只有一个文本框,即使没有提交按钮,回车也会提交;注意:textaa是个例外,如果在文本区中回车会换行,而不是提交表单;
以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发onSubmit事件,利用此事件,可以验证表单数据,从决定是否允许表单提交;阻止这个事件的默认行为或返回false就可以取消表单提交,如果不阻止,就是提交表单;
varmyForm=document.getElementById("myForm");myForm.addEventListener("submit",function(event){varinput=document.forms[0].elements[0];if(input.value==""){event.pventDefault();console.log("提交不了");}},false);
如果是DOM0级事件处理程序,也可以turnfalse;
myForm.onsubmit=function(e){varinput=document.forms[0].elements[0];if(input.value==""){console.log("input不能为空");//e.pventDefault();turnfalse;//或者}}
如果是HTML事件处理程序,可以turnfalse;
formid="myForm"name="myForm"onsubmit="turnvalidate();"scriptfunctionvalidate(){varinput=document.forms[0].elements[0];if(input.value==""){console.log("input数据不能为空");turnfalse;//或者}turntrue;}/script
submit()方法:在Javascript中,调用submit()方法也可以提交表单;而且,这种方式不需要表单包含提交按钮,在任何时候都可以正常提交表单;
varmyForm=document.getElementById("myForm");myForm.submit();
在调用submit()方法提交表单时,并不会触发onSubmit事件,因此在调用此方法之前验证表单数据;
inputid="btn"type="button"value="普通按钮"/varbtn=document.getElementById("btn");//不会触发myForm的onSubmit事件,所以数据验证必须要处理btn.addEventListener("click",function(e){if(validate()){//数据验证console.log("普通按钮提交");myForm.submit();}else{console.log("不能为空");}},false);
另外,调用此方法也不会触发约束验证,如:p输入1-10之间的整数:inputtype="number"min="1"max="10"quid//p所以,也需要在调用submit()方法前验证约束,如:
varnum=document.querySelector(input[type="number"]);if(num.quid){if(num.value==""){console.log("num不能为空");turnfalse;}}
有个误区,如果给一个提交按钮添加onSubmit事件,是无效的,它会直接提交,如:
inputid="mySubmit"type="submit"value="提交"onsubmit="turnvalidate();"/
如果为一个提交按钮添加onClick事件,可以进行表单提交验证,同时也会触发表单的onSubmit事件,例如,把上例的btn的onClick事件处理程序添加到submit提交按钮,可以看以,onClick事件先于表单的onSubmit事件触发;
另外,不要为一个表单元素的name或id的值设为submit,因为它会覆盖表单的submit方法,所以当运行时,会提示不存在的submit()函数;另外,不仅是提交按钮或普通按钮调用submit()方法能提交表单,甚至一个超链接调用submit()方法也可以提交,但要注意,需要取消超链接的默认行为:
!--ahf="javascript:void(0)"id="aBtn"提交/a--ahf="#"id="aBtn"提交/a/formscriptvaraBtn=document.getElementById("aBtn");aBtn.onclick=function(e){e.pventDefault();//或者在HTML中执行javascript:void(0)if(validate()){console.log("超链接提交");myForm.submit();}else{console.log("超链接提交数据验证不通过");}}
示例:
!--onsubmit="turnfalse"防止表单自动提交form默认为get提交--formid="myForm"onsubmit="turnfalse"p用户名:inputtype="text"name="username"id="username"//pp密码:inputtype="password"name="pwd"id="pwd"//ppbuttontype="button"onclick="login()"提交/button/p/formscriptfunctionlogin(){varusername=document.getElementById("username").value;varpwd=document.getElementById("pwd").value;if(username!=""pwd!=""){varmyForm=document.forms["myForm"];myForm.method="get";myForm.action="/login";myForm.submit();}else{console.log("数据为空");}}/script
还有一种重要的提交方式,就是Ajax提交,也就是利用XMLHttpRequest对象进行异步数据提交,它最大的特点是不会提交整个页面,只会进行局部提交。
自动提交和防止自动提交:
回车、调用提交按钮的click()方法、调用表单的submit()方法(可以在onLoad事件中,甚至可以利用setTimeout定时提交)等;如果表单中有提交按钮,可以为表单添加onsubmit=”turnfalse”;但此时,提交按钮也会失效;如果表单中没有提交按钮,不会自动提交;如果表单中只有一个文本框,但没有提交按钮,回车会自动提交,可以为表单添加onsubmit=”turnfalse”,就不会自动提交;或者添加一个隐藏的文本框,如:
inputtype="text"style="display:none;"/
也不会自动提交,注意,不是隐藏域;
或者监听文本框的onKeydown事件,如果是回车的话,不做处理,如:
inputtype="text"onkeydown="if(event.keyCode==13){turnfalse}"/
如果在表单中有提交按钮,如果表单任一个控件都处于焦点状态下,直接回车就可以提交表单,如果没有控件处于焦点状态,可以监听document的keydown事件,从而判断是否按下回车键,再进行提交,如:
document.addEventListener("keydown",function(e){if(e.keyCode==13){document.forms[0].submit();console.log("表单提交了");}},false);
防止重复提交:
提交表单时可能出现的最大问题,就是重复提交表单;解决该问题的方法有两个:在第一次提交表单后就禁用提交按钮,或者利用onSubmit事件处理程序取消后续的表单提交操作;
禁用提交按钮:
varmyForm=document.getElementById("myForm");myForm.addEventListener("submit",function(event){event.pventDefault();//为了能看到效果varbtnSubmit=event.target.elements["btnSubmit"];btnSubmit.disabled=true;},false);
注:不能通过onclick事件处理程序来实现这个功能,原因是不同浏览器之间存在“时差”:有的会在触发表单的onSubmit事件前触发onClick事件,有些相反;对于先触发onClick事件的,意味着会在提交发生之前禁用按钮,结果永远都不会提交表单,因此最好使用onSubmit事件来禁用提交按钮;此种方式不适合表单中不包含提交按钮的情况;
重置表单:
使用type特性为set的input或button两种按钮可以重置表单:
inputtype="set"value="重置"buttontype="set"重置/button
当单击重置按钮时,会触发onReset事件;利用此事件,可以在必要时取消重置操作;取消重置也就是阻止重置的默认行为,如:
varmyForm=document.getElementById("myForm");myForm.addEventListener("set",function(event){event.pventDefault();console.log("重置被禁止了");},false);
在HTML事件处理程序或DOM0级中的onReset事件中返回false也可以取消默认行为;
也可以使用Javascript调用set()方法重置,但与调用submit()方法不同,其会触发onReset事件;
varmyForm=document.getElementById("myForm");myForm.set();//...varbtn=document.getElementById("btn");btn.onclick=function(){myForm.set();//会触发onReset事件};
从用户体验角度来说,重置表单并不常见,所以有可能是意外地触发了表单重置事件,所以这种需求是很少见的,更常见的做法是提供一个取消按钮,让用户能够回到前一个页面;
表单元素(控件):
可以像访问页面中的其他元素一样,使用原生DOM方法访问表单控件;
varfields=document.getElementById("username");varfields=document.getElementsByTagName("input")[0];varfields=document.querySelectorAll(#logininput[type="radio"]);varfields=document.querySelectorAll(#logininput[type="radio"][name="color"]);
Form表单具有length属性,其返回表单元素的数量,但是不包含input元素type为“image”元素;所以也可以通过访问表单的索引或属性来访问元素,如form[0]可以取得第一个表单控件或form[“color”]或form.color获得第一个命名控件;
console.log(myForm.length);console.log(myForm[0]);console.log(myForm["username"]);console.log(myForm.username);elements:表单中所有控件的集合(HTMLCollection);varformElements=document.forms[0].elements;console.log(formElements);//HTMLFormControlsCollectionconsole.log(formElements.length);//5
其属于HTMLFormControlsCollection集合类型,继承自HTMLCollection;这个类型没有什么特别的属性和方法;注意,elements集合中不包括type等于image的input元素;
可以通过表单elements集合索引或name特性访问所有元素,如:
varform=document.getElementById("myForm");varfield1=form.elements[0];varfield2=form.elements["textbox1"];varfields=form.elements.color;varfieldCount=form.elements.length;
这种方式和直接利用表单的索引或name特性访问表单元素是一致的,相比之下,还是推荐使用这种方式,因为前者在未来有可能不支持,并且会引起一些歧义;
示例,所有表单元素的值不能为空,如:
scripttype="text/javascript"functionmyCheck(){for(vari=0;idocument.forms[0].elements.length-1;i++){if(document.forms[0].elements.value==""){alert("当前表单不能有空项");document.forms[0].elements.focus();turnfalse;}}turntrue;}/scriptformname="myForm"method="post"action="#"onSubmit="turnmyCheck()"用户名:inputtype="text"name="username"br性别:inputtype="text"name="sex"br出生时间:inputtype="text"name="birthday"brinputtype="submit"value="提交"/form
如果有多个表单控件使用同一个name,通过elements[“name”]会返回以该name命名的一个NodeList,而通过elements[index]只会返回第一个元素;
formid="myForm"pinputtype="radio"name="color"value="d"/dbr/inputtype="radio"name="color"value="gen"/dbr/inputtype="radio"name="color"value="blue"/dbr//p/formscripttype="text/javascript"varmyForm=document.getElementById("myForm");varcolors=myForm.elements["color"];//varcolors=myForm.elements.color;//或者console.log(colors);//RadioNodeListconsole.log(colors.length);//3varfirstColor=colors[0];varfirstElement=myForm.elements[0];console.log(firstColor===firstElement);//true/script
因此,在使用索引和name特性时结果有可能是不一样的;一般来说,优先使用id属性,但是name属性在HTML表单提交中有特殊的目的,一般应用在相关的复选按钮组和强制共享name属性值的、互斥的单选按钮组;另外,对于其他表单元素来说,设置name特性的目的就是为了提交到服务端,服务端根据该name特性取得这个表单元素的值;
共有的表单控件属性:
除了fieldset元素外,所有表单控件都拥有相同的一组属性;由于input类型可以表示多种表单元素,因此有些属性只适用于某些表单元素,但还有一些属性是所有表单元素所共有的:
disabled:布尔值,表示当前控件是否被禁用;form:只读,指向当前控件所属表单的指针;如果表单元素没有包含在一个form中,则值为null;name:只读,当前控件的名称;adOnly:布尔值,表示当前控件是否只读;tabIndex:表示当前控件的切换(tab)序号;type:当前控件的类型,如checkbox、radio等;value:读/写,表单元素的值,也就是将被提交给服务器的值,对file来说,是只读的,包含着文件在计算机中的路径;
以上除了form属性,都可以通过Javascript动态修改,如:
varform=document.getElementById("myForm");varfield=form.elements[0];field.value="Anothervalue";alert(field.form===form);field.focus();field.disabled=true;//不推荐,但对input是可行的field.type="checkbox";
type属性:
除了fieldset之外,所有表单元素都有type属性;对于input元素,这个值等于HTML特性type值,如:text、password、radio、checkbox、button、file、hidden、set、submit;对于其他元素,该值如下:
文件域:textaa值为textaa单选列表:select…/select值为:select-one多选列表:selectmultiple../select值为:select-multiple自定义按钮:button…/button值为:submit自定义非提交按钮:buttontype=“button”../button值为button自定义重置按钮:buttontype=“set”../button值为set自定义提交按钮:buttontype=“submit”../button值为submit
此外,input和button的type属性可以动态修改,而select元素的type属性是只读的;示例:密码框的明文和暗文:
stylespan.icon-eye{display:inline-block;width:24px;height:24px;background:url("images/eye.png")no-peat;cursor:pointer;}span.icon-eye-invisible{background-position:-24px0;}/stylepinputid="pwd"name="pwd"type="password"/spanclass="icon-eyeicon-eye-invisible"/span/pscriptvariconEye=document.querySelector("span.icon-eye");iconEye.addEventListener("click",function(event){varp=document.getElementsByTagName("p")[0];varpwd=document.getElementById("pwd");if(p.classList.toggle("icon-eye-invisible")){pwd.type="text";}else{pwd.type="password";}},false);/script
示例:在弹出窗口提交表单
scriptfunctionpopupSend(oForm){if(oForm.methodoForm.method.toLowerCase()!=="get"){alert("只允许GET方式提交");turn;}varoField,sFieldType,nFile,sSearch="";for(vari=0;ioForm.elements.length;i++){oField=oForm.elements;if(!oField.hasAttribute("name"))continue;//sFieldType=oField.nodeName.toUpperCase()==="INPUT"?oField.getAttribute("type").toUpperCase():"TEXT";sFieldType=oField.nodeName.toUpperCase()==="INPUT"?oField.type.toUpperCase():"TEXT";if(sFieldType==="FILE"){for(nFile=0;nFileoField.files.length;sSearch+=""+escape(oField.name)+"="+escape(oField.files[nFile++].name));}else{sSearch+=""+escape(oField.name)+"="+escape(oField.value);}}open(oForm.action.place(/(?:\?.*)?/,sSearch.place(/^/,"?")),"submit-"+(oForm.name
Math.floor(Math.random()*1e6)),"sizable=yes,scrollbars=yes,status=yes");}/scriptformid="myForm"name="myForm"action="demo.php"method="get"pFirstName:inputtype="text"name="firstname"/br/LastName:inputtype="text"name="lastname"/br/Password:inputtype="password"name="pwd"br/Photo:inputtype="file"name="photo"br/inputtype="radio"name="sex"value="male"Maleinputtype="radio"name="sex"value="female"Female/ppinputtype="checkbox"name="vehicle"value="Bike"自行车br/inputtype="checkbox"name="vehicle"value="Car"汽车/ppinputtype="submit"value="提交"/p/formscriptvarmyForm=document.getElementById("myForm");myForm.addEventListener("submit",function(e){popupSend(this);e.pventDefault();})/script
共有的表单控件方法:
每个表单元素都有两个方法:focus()和blur();其中,focus()方法用于获得焦点,即激活表单元素,使其可以响应键盘事件,如:
window.onload=function(e){document.forms[0].elements[0].focus();}
默认情况下,只有表单元素可以获得焦点;对于其他元素,可以设置其tabIndex设置为-1,然后再调用focus()方法,也可以让这些元素获得焦点;
HTML5为表单控件新增了一个autofocus属性,在支持这个属性的浏览器中,只要设置这个属性,不用Javascript就能自动把焦点转移到相应的控件上,如:
inputtype="text"autofocus/
如果在HTML中已经为元素设置这个属性了,就不用在Javascript中调用focus()了,因此有必要在调用focus()之前先检测是否设置了该属性,如:
window.addEventListener("load",function(event){varelement=document.forms[0].elements[0];if(element.autofocus!==true){element.focus();console.log("Jsfocus");}});
blur()方法:从元素中移除焦点;
与focus()方法相对的是blur()方法,它的作用是从元素中移走焦点;在调用blur()时,并不会把焦点转移到某个特定的元素上,其仅仅是将焦点从调用这个方法的元素上面移走而已,如:
document.forms[0].elements[0].blur();
共有的表单元素事件:
当用户与表单元素交互时它们往往会触发鼠标、键盘或其他HTML等常规事件,除此之外,表单元素还支持以下3个事件:
blur:失去焦点时时触发;focus:获得焦点时触发;change:对于input和textaa元素,在它们失去焦点且value值改变时触发;对于select元素,在其选项改变时触发;
如:
vartextbox=document.forms[0].elements[0];textbox.addEventListener("focus",function(event){if(event.target.style.backgroundColor!="d"){event.target.style.backgroundColor="yellow";}});textbox.addEventListener("blur",function(event){if(/[\d]/.test(event.target.value)){event.target.style.backgroundColor="gen";}else{event.target.style.backgroundColor="d";}console.log("blur");});textbox.addEventListener("change",function(event){if(/[\d]/.test(event.target.value)){event.target.style.backgroundColor="gen";}else{event.target.style.backgroundColor="d";}console.log("change");});
需要强调的是,change事件对于input和textaa元素,在它们失去焦点且value值改变时触发;对于select元素,在其选项改变时触发,如:
varselectbox=document.forms[0].elements["mySelect"];selectbox.addEventListener("change",function(event){console.log(this.options[this.selectedIndex].value);});selectbox.addEventListener("blur",function(e){console.log("selectblur");});
当改变选项时,会触发change事件,但此时,select仍处于焦点状态,当其失去焦点时,才会触发blur事件,这一点,有input有很大区别;
事件处理程序中的this:事件处理程序中的this是触发该事件的元素的一个引用;例如,可以通过this.form来取得其所在的Form对象的引用;通过this.form.x来获取该表单中其它的表单元素;
textbox.addEventListener("focus",function(event){console.log(this);console.log(this===event.target);//trueconsole.log(this.form);console.log(this.form.elements["city"]);});