您的位置 首页 php

第63节 Form表单-Web前端开发之JavaScript-王唯

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。

在HTML中,表单是用<form>元素来表示的,它与其他各种各样的表单输入元素,如<input>、<select>和<button>在客户端编程中有着重要的地位。

表单的作用是,通过表单元素收集用户的输入,再将这些输入数据提交给服务器,服务器处理接收到的数据并生成一个新的HTML页面显示在客户端;在以前,表单提交数据、服务器返回数据,此时客户端与服务端并没有其他交互行为,因此,数据提交交互性差、服务器负担重。

Javascript的最初的一个应用,就是分担服务器处理表单的任务,打破处处依赖服务器的局面;虽然现在Web和Javascript有了长足的发展,但Web表单的变化并不明显;特别是一些常见的形式,web表单并没有提供特别好的方案;最常见的操作,是使用Javascript增强了一些标准表单控件的默认行为。

表单及其控件都是HTML元素,可以使用标准的 DOM 技术来操作它们,另外表单已经脚本化了,也有专门的API,所以在表单编程中,最好使用表单API;

取得Form表单对象:

取得<form>元素引用的方式:最常用的方式就是将它看成与其他元素一样,使用getElementById()或getElementsByTagName()等标准的DOM技术;

 var form = document.getElementById("myForm");
var form = document.getElementsByTagName("form")[0];  

通过document.forms集合可以取得页面中所有表单集合,在这个 HTMLCollection 集合中,可以通过数值索引或name或id值来取得特定的表单:

 var firstForm = document.forms[0];
var myForm = document.forms["myForm"];  

较早的浏览器或者那些支持向后兼容的浏览器中,会把每个设置了name特性的表单作为属性保存在document对象中,如:

 var form = document.myForm;  

注:不推荐这种方式;
注:可以同时为表单指定id和name属性,但它们可以设置不同的值;

表单属性和方法:

表单<form>在HTML中具有action、encoding、method和target等属性,在Javascript中,它是HTMLFormElement类型,继承了 HTML Element,因而与其他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的target
  • reset():方法,将所有表单域重置为默认值;
  • submit():方法,提交表单;

以上的大部分属性是可写的;

 <script>
console.log(form.action); // demo.html
form.action = "/login";
console.log(form.action); // login
// 获取表单信息
function getFormInfo(){
var info;
var form = 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;
}
function setFormInfo(form){
form.name = "yourForm"
form.method = "GET";
form.action = "/member";
form.acceptCharset = "gb2312";
form.enctype = "multipart/form-data";
form.target = "_blank";
var pwd = document.createElement("input");
pwd.type = "password";
pwd.id = "pwd";
form.appendChild(pwd);
}
</script>
<form name="myForm" id="myForm" action="/login" method="POST">
<p><input type="text" id="username" name="username" /></p>
<p><input type="button" value="表单信息" onclick="getFormInfo()" />
<input type="button" value="设置表单" onclick="setFormInfo(this.form)" /></p>
<p><textarea id="txt"></textarea></p>
</form>  

也可以使用DOM方法动态创建表单,如:

 var form = document.createElement("form");
document.body.appendChild(form);
form.name = "myform";
form.action = "/login";
form.method = "POST";
// form.submit();
// 或者
var btn = document.createElement("input");
btn.type = "submit";
btn.value = "提交";
form.appendChild(btn);  

Submit提交表单:
使用<input>的type特性为”submit”或”image”就可以提交表单,或者<button>也可以提交表单;

 <input type="submit" value="提交" />
<input type="image" src="images/submit.png" />
<button type="submit">提交表单</button>  

以上的按钮,只要在任何表单元素拥有焦点的情况下,按回车就可以提交表单;如果表单里没有提交按钮,按回车键不会提交表单,但有个特例,如果只有一个文本框,即使没有提交按钮,回车也会提交;
注意:textarea是个例外,如果在文本区中回车会换行,而不是提交表单;

以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发onSubmit事件,利用此事件,可以验证表单数据,从决定是否允许表单提交;阻止这个事件的默认行为或返回false就可以取消表单提交,如果不阻止,就是提交表单;

 var myForm = document.getElementById("myForm");
myForm.addEventListener("submit", function(event){
var input = document.forms[0].elements[0];
if(input.value == ""){
event.preventDefault();
console.log("提交不了");
}
},false);  

如果是DOM0级事件处理程序,也可以return false;

 myForm.onsubmit = function(e){
var input = document.forms[0].elements[0];
if(input.value == ""){
console.log("input不能为空");
// e. prevent Default();
return false; // 或者
}
}  

如果是HTML事件处理程序,可以return false;

 <form id="myForm" name="myForm" onsubmit="return validate();">
<script>
function validate(){
var input = document.forms[0].elements[0];
if(input.value == ""){
console.log("input数据不能为空");
return false; // 或者
}
return true;
}
</script>  

submit()方法:
在Javascript中,调用submit()方法也可以提交表单;而且,这种方式不需要表单包含提交按钮,在任何时候都可以正常提交表单;

 var myForm = document.getElementById("myForm");
myForm.submit();  

在调用submit()方法提交表单时,并不会触发onSubmit事件,因此在调用此方法之前验证表单数据;

 <input id="btn" type="button" value="普通按钮" />
var btn = document.getElementById("btn");
// 不会触发myForm的onSubmit事件,所以数据验证必须要处理
btn.addEventListener("click",function(e){
if(validate()){ // 数据验证
console.log("普通按钮提交");
myForm.submit();
}else{
console.log("不能为空");
}
},false);  

另外,调用此方法也不会触发约束验证,如:
<p>输入1-10之间的整数:<input type=”number” min=”1″ max=”10″ required /></p>
所以,也需要在调用submit()方法前验证约束,如:

 var num = document.querySelector('input[type="number"]');
if(num.required){
if(num.value == ""){
console.log("num不能为空");
return false;
}
}  

有个误区,如果给一个提交按钮添加onSubmit事件,是无效的,它会直接提交,如:

 <input id="mySubmit" type="submit" value="提交" onsubmit="return validate();" />  

如果为一个提交按钮添加onClick事件,可以进行表单提交验证,同时也会触发表单的onSubmit事件,例如,把上例的btn的onClick事件处理程序添加到submit提交按钮,可以看以,onClick事件先于表单的onSubmit事件触发;

另外,不要为一个表单元素的name或id的值设为submit,因为它会覆盖表单的submit方法,所以当运行时,会提示不存在的submit()函数;
另外,不仅是提交按钮或普通按钮调用submit()方法能提交表单,甚至一个超链接调用submit()方法也可以提交,但要注意,需要取消超链接的默认行为:

 <!-- <a href="javascript:void(0)" id="aBtn">提交</a> -->
<a href="#" id="aBtn">提交</a>
</form>
<script>
var aBtn = document.getElementById("aBtn");
aBtn.onclick = function(e){
e.preventDefault(); // 或者在HTML中执行javascript:void(0)
if(validate()){
console.log("超链接提交");
myForm.submit();
}else{
console.log("超链接提交数据验证不通过");
}
}  

示例:

 <!-- onsubmit="return false" 防止表单自动提交
form 默认为get提交 -->
<form id="myForm" onsubmit="return false">
<p>用户名:<input type="text" name="username" id="username" /></p>
<p>密码:<input type="password" name="pwd" id="pwd" /></p>
<p><button type="button" onclick="login()">提交</button></p>
</form>
<script>
function login(){
var username = document.getElementById("username").value;
var pwd = document.getElementById("pwd").value;
if(username != "" && pwd !=""){
var myForm = document.forms["myForm"];
myForm.method = "get";
myForm.action = "/login";
myForm.submit();
}else{
console.log("数据为空");
}
}
</script>  

还有一种重要的提交方式,就是Ajax提交,也就是利用 XMLHttpRequest 对象进行异步数据提交,它最大的特点是不会提交整个页面,只会进行局部提交。

自动提交和防止自动提交:

回车、调用提交按钮的click()方法、调用表单的submit()方法(可以在onLoad事件中,甚至可以利用setTimeout定时提交)等;
如果表单中有提交按钮,可以为表单添加onsubmit=”return false”;但此时,提交按钮也会失效;
如果表单中没有提交按钮,不会自动提交;
如果表单中只有一个文本框,但没有提交按钮,回车会自动提交,可以为表单添加onsubmit=”return false”,就不会自动提交;或者添加一个隐藏的文本框,如:

 <input type="text" style="display: none;" />  

也不会自动提交,注意,不是隐藏域;

或者监听文本框的onKeydown事件,如果是回车的话,不做处理,如:

 <input type="text" onkeydown="if(event.keyCode==13){return false}" />  

如果在表单中有提交按钮,如果表单任一个控件都处于焦点状态下,直接回车就可以提交表单,如果没有控件处于焦点状态,可以监听document的keydown事件,从而判断是否按下回车键,再进行提交,如:

 document.addEventListener("keydown", function(e){
if(e.keyCode == 13){
document.forms[0].submit();
console.log("表单提交了");
}
},false);  

防止重复提交:

提交表单时可能出现的最大问题,就是重复提交表单;解决该问题的方法有两个:在第一次提交表单后就禁用提交按钮,或者利用onSubmit事件处理程序取消后续的表单提交操作;

禁用提交按钮:

 var myForm = document.getElementById("myForm");
myForm.addEventListener("submit", function(event){
event.preventDefault(); // 为了能看到效果
var btnSubmit = event.target.elements["btnSubmit"];
btnSubmit.disabled = true;
},false);  

注:不能通过onclick事件处理程序来实现这个功能,原因是不同浏览器之间存在“时差”:有的会在触发表单的onSubmit事件前触发onClick事件,有些相反;对于先触发onClick事件的,意味着会在提交发生之前禁用按钮,结果永远都不会提交表单,因此最好使用onSubmit事件来禁用提交按钮;
此种方式不适合表单中不包含提交按钮的情况;

重置表单:

使用type特性为reset的<input>或<button>两种按钮可以重置表单:

 <input type="reset" value="重置">
<button type="reset">重置</button>  

当单击重置按钮时,会触发onReset事件;利用此事件,可以在必要时取消重置操作;取消重置也就是阻止重置的默认行为,如:

 var myForm = document.getElementById("myForm");
myForm.addEventListener("reset", function(event){
event.preventDefault();
console.log("重置被禁止了");
},false);  

在HTML事件处理程序或DOM0级中的onReset事件中返回false也可以取消默认行为;

也可以使用Javascript调用reset()方法重置,但与调用submit()方法不同,其会触发onReset事件;

 var myForm = document.getElementById("myForm");
myForm.reset();
//...
var btn = document.getElementById("btn");
btn.onclick = function(){
myForm.reset(); // 会触发onReset事件
};  

从用户体验角度来说,重置表单并不常见,所以有可能是意外地触发了表单重置事件,所以这种需求是很少见的,更常见的做法是提供一个取消按钮,让用户能够回到前一个页面;

表单元素(控件):

可以像访问页面中的其他元素一样,使用原生DOM方法访问表单控件;

 var fields = document.getElementById("username");
var fields = document.getElementsByTagName("input")[0];
var fields = document.querySelectorAll('#login input[type="radio"]');
var fields = document.querySelectorAll('#login input[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);
var formElements = document.forms[0].elements;
console.log(formElements); // HTMLFormControlsCollection
console.log(formElements.length); // 5  

其属于HTMLFormControlsCollection集合类型,继承自HTMLCollection;这个类型没有什么特别的属性和方法;
注意,elements集合中不包括type等于image的input元素;

可以通过表单elements集合索引或name特性访问所有元素,如:

 var form = document.getElementById("myForm");
var field1 = form.elements[0];
var field2 = form.elements["textbox1"];
var fields = form.elements.color;
var fieldCount = form.elements.length;  

这种方式和直接利用表单的索引或name特性访问表单元素是一致的,相比之下,还是推荐使用这种方式,因为前者在未来有可能不支持,并且会引起一些歧义;

示例,所有表单元素的值不能为空,如:

 <script type="text/javascript">
function myCheck(){
for(var i=0;i<document.forms[0].elements.length-1;i++){
if(document.forms[0].elements[i].value==""){
alert("当前表单不能有空项");
document.forms[0].elements[i].focus();
return false;
}
}
return true;
}
</script>
<form name="myForm" method="post" action="#" onSubmit="return myCheck()">
用户名:<input type="text" name="username"><br>
性别:<input type="text" name="sex"><br>
出生时间:<input type="text" name="birthday"><br>
<input type="submit" value="提交">
</form>  

如果有多个表单控件使用同一个name,通过elements[“name”]会返回以该name命名的一个NodeList,而通过elements[index]只会返回第一个元素;

 <form id="myForm">
<p>
<input type="radio" name="color" value="red" />red<br/>
<input type="radio" name="color" value="green" />red<br/>
<input type="radio" name="color" value="blue" />red<br/>
</p>
</form>
<script type="text/javascript">
var myForm = document.getElementById("myForm");
var colors = myForm.elements["color"];
// var colors = myForm.elements.color; // 或者
console.log(colors); // RadioNodeList
console.log(colors.length); // 3
var firstColor = colors[0];
var firstElement = myForm.elements[0];
console.log(firstColor === firstElement); // true
</script>  

因此,在使用索引和name特性时结果有可能是不一样的;一般来说,优先使用id属性,但是name属性在HTML表单提交中有特殊的目的,一般应用在相关的复选按钮组和强制共享name属性值的、互斥的单选按钮组;另外,对于其他表单元素来说,设置name特性的目的就是为了提交到服务端,服务端根据该name特性取得这个表单元素的值;

共有的表单控件属性:

除了<fieldset>元素外,所有表单控件都拥有相同的一组属性;由于<input>类型可以表示多种表单元素,因此有些属性只适用于某些表单元素,但还有一些属性是所有表单元素所共有的:

  • disabled:布尔值,表示当前控件是否被禁用;
  • form:只读,指向当前控件所属表单的指针;如果表单元素没有包含在一个<form>中,则值为null;
  • name:只读,当前控件的名称;
  • readOnly:布尔值,表示当前控件是否只读;
  • tabIndex:表示当前控件的切换(tab)序号;
  • type:当前控件的类型,如checkbox、radio等;
  • value:读/写,表单元素的值,也就是将被提交给服务器的值,对file来说,是只读的,包含着文件在计算机中的路径;

以上除了form属性,都可以通过Javascript动态修改,如:

 var form = document.getElementById("myForm");
var field = form.elements[0];
field.value = "Another value";
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、reset、submit;对于其他元素,该值如下:

  • 文件域:<textarea> 值为textarea
  • 单选列表:<select>…</select> 值为:select-one
  • 多选列表:<select multiple>..</select> 值为:select-multiple
  • 自定义按钮:<button>…</button> 值为:submit
  • 自定义非提交按钮:<button type=“button”>..</button> 值为button
  • 自定义重置按钮:<button type=“reset”>..</button> 值为reset
  • 自定义提交按钮:<button type=“submit”>..</button> 值为submit

此外,<input>和<button>的type属性可以动态修改,而<select>元素的type属性是只读的;示例:密码框的明文和暗文:

 <style>
span.icon-eye{
display: inline-block; width:24px; height: 24px;
background: url("images/eye.png") no-repeat; cursor: pointer;
}
span.icon-eye-invisible{
background-position: -24px 0;
}
</style>
<p><input id="pwd" name="pwd" type="password" /><span class="icon-eye icon-eye-invisible"></span></p>
<script>
var iconEye = document.querySelector("span.icon-eye");
iconEye.addEventListener("click", function(event){
var p = document.getElementsByTagName("p")[0];
var pwd = document.getElementById("pwd");
if(p.classList.toggle("icon-eye-invisible")){
pwd.type = "text";
}else{
pwd.type = "password";
}
},false);
</script>  

示例:在弹出窗口提交表单

 <script>
function popupSend(oForm){
if(oForm.method && oForm.method.toLowerCase() !== "get"){
alert("只允许GET方式提交");
return;
}
var oField, sFieldType, nFile, sSearch = "";
for(var i = 0; i < oForm.elements.length; i++){
oField = oForm.elements[i];
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; nFile < oField.files.length; sSearch += "&" + escape(oField.name) + "=" + escape(oField.files[nFile++].name));
}else{
sSearch += "&" + escape(oField.name) + "=" + escape(oField.value);
}
}
open(oForm.action.replace(/(?:\?.*)?$/, sSearch.replace(/^&/, "?")), "submit-" + (oForm.name || Math.floor(Math.random() * 1e6)), "resizable=yes,scrollbars=yes,status=yes");
}
</script>
<form id="myForm" name="myForm" action="demo.php" method="get">
<p>First Name:<input type="text" name="firstname" /><br/>
Last Name:<input type="text" name="lastname" /><br/>
Password:<input type="password" name="pwd"><br/>
Photo:<input type="file" name="photo"><br/>
<input type="radio" name="sex" value="male">Male
<input type="radio" name="sex" value="female">Female</p>
<p><input type="checkbox" name="vehicle" value="Bike">自行车<br/>
<input type="checkbox" name="vehicle" value="Car">汽车</p>
<p><input type="submit" value="提交"></p>
</form>
<script>
var myForm = document.getElementById("myForm");
myForm.addEventListener("submit", function(e){
popupSend(this);
e.preventDefault();
})
</script>  

共有的表单控件方法:

每个表单元素都有两个方法:focus()和blur();其中,focus()方法用于获得焦点,即激活表单元素,使其可以响应键盘事件,如:

 window.onload = function(e){
document.forms[0].elements[0].focus();
}  

默认情况下,只有表单元素可以获得焦点;对于其他元素,可以设置其tabIndex设置为-1,然后再调用focus() 方法,也可以让这些元素获得焦点;

HTML5为表单控件新增了一个autofocus属性,在支持这个属性的浏览器中,只要设置这个属性,不用Javascript就能自动把焦点转移到相应的控件上,如:

 <input type="text" autofocus />  

如果在HTML中已经为元素设置这个属性了,就不用在Javascript中调用focus()了,因此有必要在调用focus()之前先检测是否设置了该属性,如:

 window.addEventListener("load", function(event){
var element = document.forms[0].elements[0];
if(element.autofocus !== true){
element.focus();
console.log("Js focus");
}
});  

blur()方法:从元素中移除焦点;

与focus()方法相对的是blur()方法,它的作用是从元素中移走焦点;在调用blur()时,并不会把焦点转移到某个特定的元素上,其仅仅是将焦点从调用这个方法的元素上面移走而已,如:

 document.forms[0].elements[0].blur();  

共有的表单元素事件:

当用户与表单元素交互时它们往往会触发鼠标、键盘或其他HTML等常规事件,除此之外,表单元素还支持以下3个事件:

  • blur:失去焦点时时触发;
  • focus:获得焦点时触发;
  • change:对于<input>和<textarea>元素,在它们失去焦点且value值改变时触发;对于<select>元素,在其选项改变时触发;

如:

 var textbox = document.forms[0].elements[0];
textbox.addEventListener("focus", function(event){
if(event.target.style.backgroundColor != "red"){
event.target.style.backgroundColor = "yellow";
}
});
textbox.addEventListener("blur", function(event){
if(/[\d]/.test(event.target.value)){
event.target.style.backgroundColor = "green";
}else{
event.target.style.backgroundColor = "red";
}
console.log("blur");
});
textbox.addEventListener("change", function(event){
if(/[\d]/.test(event.target.value)){
event.target.style.backgroundColor = "green";
}else{
event.target.style.backgroundColor = "red";
}
console.log("change");
});  

需要强调的是,change事件对于<input>和<textarea>元素,在它们失去焦点且value值改变时触发;对于<select>元素,在其选项改变时触发,如:

 var selectbox = document.forms[0].elements["mySelect"];
selectbox.addEventListener("change", function(event){
console.log(this.options[this.selectedIndex].value);
});
selectbox.addEventListener("blur", function(e){
console.log("select blur");
});  

当改变选项时,会触发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); // true
console.log(this.form);
console.log(this.form.elements["city"]);
});  

文章来源:智云一二三科技

文章标题:第63节 Form表单-Web前端开发之JavaScript-王唯

文章地址:https://www.zhihuclub.com/151650.shtml

关于作者: 智云科技

热门文章

网站地图