DOM编程艺术(表单操作)

本文详细介绍了HTML表单的设计步骤,包括表单元素的选择、配置及提交方式,并深入探讨了表单验证的方法,如必填项检查、自定义验证信息等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、表单元素

一、编写表单的步骤:

1、构建表单----------------》2、服务器处理(主要是提供负责接收数据的接口,用来处理数据的存储等信息)------------》3、配置表单

示例:

披萨预定表单:

	<form action="">
		<p><label>姓名:<input></label></p><!--用label标签来做元素的关联-->
		<p><label>电话:<input type="tel"></label></p>
		<p><label>邮箱:<input type="email"></label></p>
		<fieldset>
			<legend> 披萨大小 </legend>
			<label><input type="radio" name="size"> 小 </label>
			<label><input type="radio" name="size"> 中 </label>
			<label><input type="radio" name="size"> 大 </label>
		</fieldset>
		<fieldset>
			<legend> 披萨配料 </legend>
			<label><input type="checkbox"> 熏肉 </label>
			<label><input type="checkbox"> 奶酪 </label>
			<label><input type="checkbox"> 洋葱 </label>
			<label><input type="checkbox"> 蘑菇 </label>
		</fieldset>
		<p><label>配送时间:<input type="time" min="11:00" max="21:00" step="900"></label></p>
		<p><button>提交订单</button></p>
	</form>
配置表单:
	<form method="post" action="https://pizza.example.com/order" enctype="application/x-www-form-urlencoded">
	    <!--action属性配置接收数据的地址,enctype指定编码方式-->
		<p><label>姓名:<input name="custname"></label></p>
		<p><label>电话:<input type="tel" name="custtel"></label></p>
		<p><label>邮箱:<input type="email" name="custemail"></label></p>
		<fieldset>
			<legend> 披萨大小 </legend>
			<label><input type="radio" name="size" value="small"> 小 </label>
			<label><input type="radio" name="size" value="medium"> 中 </label>
			<label><input type="radio" name="size" value="large"> 大 </label>
		</fieldset>
		<fieldset>
			<legend> 披萨配料 </legend>
			<label><input type="checkbox" name="topping" value="bacon"> 熏肉 </label>
			<label><input type="checkbox" name="topping" value="cheese"> 奶酪 </label>
			<label><input type="checkbox" name="topping" value="onion"> 洋葱 </label>
			<label><input type="checkbox" name="topping" value="mushroom"> 蘑菇 </label>
		</fieldset>
		<p><label>配送时间:<input type="time" name="delivery" min="11:00" max="21:00" step="900"></label></p>
		<p><button>提交订单</button></p>
	</form>

二、表单元素

1、form标签:

           name属性:主要用于之后的API:var pizzaForm = document.forms.pizza;

           autocomplete属性:属性值有on / off ,当值为on时,鼠标焦点到“姓名”的输入框中时,下面会出现之前输入过的值以备候选,候选值也会根据输入情况更改;当值为off时,也出现提示,就与浏览器的设置有关了。

           elements:包含:①该表单子孙表单控件(除图片按钮(<input type="image">))(表单控件有 button、fieldset、input、keygen、object、output、select、textarea)

                                            ②归属于该表单的表单控件(除图片按钮),示例看下面的代码:(动态节点集合)

	<form id="f">
		<p><label><input name="a"></label></p><!--符合①-->
		<p><select name="b"><option>1</option></select></p><!--符合①-->
	</form>
	<p><label><input name="c"></label></p><!--不符合-->
	<p><label><input name="d" form="f"></label></p><!--符合②-->
             length:elements.length
2、获取form表单中的表单控件:
	<form name="test">
		<input name="a" />
		<input name="b" />
	</form>
	testForm.elements[0];

	testForm.elements['a'];

	testForm[0];   //form[index]

	testForm['a'];   //form[name]

*form[name]:

---------------------返回id或name为指定名称的表单控件(除图片按钮)

---------------------如果结果为空、则返回id为指定名称的img元素

---------------------如果有多个同名元素,则返回这些元素的动态节点集合

---------------------一旦用指定名称取过该元素,则不管该元素的id或者name怎么变化,只要节点还在页面上均可使用原名称获取该元素:

                                  

	<form name="test">
		<input name="a" />
	</form>

	testForm['a'];

	testForm.elements['a'];

	testForm['a'].name = 'b';

	testForm['a'];//<input name="b" />

	testForm.elements['a'];//null

3、接口:

-----reset()

可重置元素:input、keygen、output、select、textarea

触发表单reset事件,阻止该事件的默认行为可取消重置

元素重置时不再触发元素上的change和input事件

示例:有一个文件选择器,已经选择了一个文件,现在我们想要把这个选择的文件删除:

	<form name="file">
		<input name="image" type="file" />
	</form>
	<script>
	fileForm['image'].value = ''; //根据浏览器的安全限制,这样做是无法删除已选中文件的
	fileForm.reset();
	</script>

-----submit()

-----checkValidity()

4、label标签

	<label for="txtId" form="formId"></label>

主要通过for属性关联到相关的表单控件上

①、htmlFor

-------------------关联表单控件激活行为,也就是说关联之后,点击label的行为与表单控件的行为保持一致

-------------------可关联元素:button、input(除hidden外)、keygen、meter、output、progress、select、textarea

	<form class="f-hidden">
		<input id="file" name="image" type="file"><!--0*0尺寸不可见-->
	</form>
	<label for="file" class="m-upload">选择图片</label><!--上传按钮效果,这里最重要的就是将for属性指到input id为file,这里点击label就有与点击input标签选择文件行为相同的操作-->

②control

---------------------如果指定了for属性,则为该for属性对应ID的可关联元素

---------------------如果没有指定for属性,则为第一个子孙可关联元素

	<label for="txtId">文字<input name="desc"></label>
	<span id="txtId">only for test content here</span>

在上面代码中:a、指定了for属性

                            b、for属性对应ID的元素span为非可关联元素

                            c、label.control——>null

③form

----------------------关联归属表单

----------------------可关联元素:button,fieldset,input,keygen,label,object,output,select,textarea

----------------------只读属性,不可在程序中修改

label.setAttribute('form','newFormId');

5、input标签

①type

--------------------控件外观

--------------------数据类型

--------------------默认为text

示例:本地图片预览:

	<input type="file" accept="image/*" multiple>

multiple表示可以多选,accept支持的属性值:audio/*、video/*、image/*、不带“;”的MIME type、以“.”开始的文件后缀名。
	file.addEventListener("change", function(event){
		var files = Array.prototype.slice.call(event.target.files,0);
		files.forEach(function(item){
			<strong>file2dataurl</strong>(item,function(url){
				var image = new Image();
				parent.appendChild(image);
				image.src = url;
			});
		});
	});

file2dataurl接口没有实现,思考

6、select标签

	<select name="course">
		<option>课程选择</option>
		<optgroup label="1. DOM基础">
			<option value="1.1">1.1 文档树</option>
			<option value="1.2">1.2 节点操作</option>
			<option value="1.3">1.3 元素遍历</option>
			<option value="1.4">1.4 样式操作</option>
			<option value="1.5">1.5 属性操作</option>
			<option value="1.6">1.6 表单操作</option>
		</optgroup>
		<optgroup label="2. 事件模型">
			<option value="2.1">2.1 事件类型</option>
			<option value="2.2">2.2 事件模型</option>
			<option value="2.3">2.3 事件应用</option>
		</optgroup>
	</select>

①select属性和接口:

select:name、value、multiple、options、selectedOptions、selectedIndex、add(element[,before])、remove([index])

optgroup:disabled 、label(对相关性较大的做分组,disabled表示当前分组里的都是不可选的)

option:disabled、label、value、text、index、selected、defaultSelected

*创建选项:

------document.createElement

------new Option([text[,value[,defaultSelected[,selected]]]])

这两个方式的效果是一样的。

 new Option('1.2 节点操作','1.2');
//两者等价
 var option = document.createElement('option');
 option.value = '1.2';
 option.textContent = '1.2 节点操作';

生成了:

<option value="1.2">1.2 节点操作</option>

*添加选项:

------insertAdjacentElement

------select.add

//需插入的节点
var option = new Option('1.0 概述','1.0');
//opt11为参照点
opt11.insertAdjacentElement(option,'beforeBegin');
select.add(option,opt11);

*删除选项

------removeChild

------select.remove

opt12.parentNode.removeChild(opt12);
//2为待删除节点的索引值
select.remove(2);

示例:级联下拉选择器

案例描述:两个select选择器,后一个选择器根据前一个选择的所选的内容发生改变:

涉及到的知识点:onchange、remove、add

	<form name="course">
		<select name="chapter">
			<option>请选择章目录</option>
		</select>
		<select name="section">
			<option>请选择节目录</option>
		</select>
	</form>

接下来对章节的数据内容进行定义:
	var chapters = [
		{text:'1. DOM基础',value:'1'},
		{text:'2. 事件模型',value:'2'}
	];
	var sections = {
		1:[
			{text:'1.1 文档树',value:'1.1'},
			{text:'1.2 节点操作',value:'1.2'},
			{text:'1.3 元素遍历',value:'1.3'},
			{text:'1.4 样式操作',value:'1.4'},
			{text:'1.5 属性操作',value:'1.5'},
			{text:'1.6 表单操作',value:'1.6'}
		],
		2:[
			{text:'2.1 事件类型',value:'2.1'},
			{text:'2.2 事件模型',value:'2.2'},
			{text:'2.3 事件应用',value:'2.3'}
		]
	};

提取通用的接口:
	//用选定的数据列表填充选择器
	function fillSelect(select,list){
		for(var i=select.length-1; i>0; i --){
			select.remove(i);
		}
		list.forEach(function(data){
			var option = new Option(data.text,data.value);
			select.add(option);
		});
	}
	//对首级的选择器做填充
	fillSelect(chapterSelect,chapters);
	//章的选择器会导致节的选择器的变化
	chapterSelect.addEventListener('change', function(event){
		var value = event.target.value,
			list = sections[value] || [];
		fillSelect(sectionSelect,list);
	});

7、textarea

①属性和接口:

name、value、select()(对内容全选之后就会触发这个接口)、selectionStart、selectionEnd、selectionDirection(用来控制shift+方向键的行为,当值为forward时,改变的是selectionEnd,值为backward时,改变的是selectionStart)、setSelectionRange(start,end[,direction])、setRangeText(replacement[,start,end[,mode]])

@输入提示

描述:比如QQ聊天时,对话框中输入@就会出现好友列表,可以选择其中的一个值填充到@后面,之后再输入其他文本。

涉及到的知识点:oninput、selectionStart、setRangeText

	textarea.addEventListener(
		'input',function(event){
			var target = event.target,
				cursor = target.selectionStart;
			if(target.value.charAt(cursor - 1) === '@'){
				doShowAtList(function(name){
					var end = cursor+name.length;
					target.setRangeText(name,cursor,end,'end');
				});
			}
		}
	);


2、表单验证

	<form method="post" action="https://pizza.example.com/order" enctype="application/x-www-form-urlencoded">
	    <!--action属性配置接收数据的地址,enctype指定编码方式-->
		<p><label>姓名:<input name="custname" required></label></p>
		<p><label>电话:<input type="tel" name="custtel" required></label></p>
		<p><label>邮箱:<input type="email" name="custemail"></label></p>
		<fieldset>
			<legend> 披萨大小 </legend>
			<label><input type="radio" name="size" value="small" required> 小 </label>
			<label><input type="radio" name="size" value="medium" required> 中 </label>
			<label><input type="radio" name="size" value="large" required> 大 </label>
		</fieldset>
		<fieldset>
			<legend> 披萨配料 </legend>
			<label><input type="checkbox" name="topping" value="bacon"> 熏肉 </label>
			<label><input type="checkbox" name="topping" value="cheese"> 奶酪 </label>
			<label><input type="checkbox" name="topping" value="onion"> 洋葱 </label>
			<label><input type="checkbox" name="topping" value="mushroom"> 蘑菇 </label>
		</fieldset>
		<p><label>配送时间:<input type="time" name="delivery" min="11:00" max="21:00" step="900"></label></p>
		<p><button>提交订单</button></p>
	</form>

在必须要填写的表单标签中添加“required”属性,当用户没有填写完整时点击提交按钮就会马上返回提示信息。

一、验证:

1、验证元素:

---------可验证元素:button、input、select、textarea

---------以下情况不做验证:

                 ①input && type = {hidden、reset、button}

                 ②button && type = {reset、button}

                 ③input / textarea && readonly

                 ④作为datalist的子孙节点

                 ⑤disabled

2、验证涉及到的属性或接口:

element:willValidate(判断表单提交时元素是否会被验证)、checkValidity()(用来验证元素,通过的话就会返回true,否则会触发validate事件)、validity、validationMessage(显示验证异常信息)、setCustomValidity(message)

①validity:存储了验证过程中可能出现的错误验证信息

②自定义异常:

-------oninvalid事件

-------setCustomValidity接口

a、将必填字段的提示信息自定义设置,默认为“请填写此字段。”,改为“请输入姓名!”

	<form action="./api" method="post">
		<p><label>姓名:<input name="username" required /></label></p>
		<p><button>submit</button></p>
	</form>

	input.addEventListener('invalid', function(event){
		var target = event.target;
		//如果是没用输入的话,就自定义异常信息
		if(target.validity.valueMissing){
			target.setCustomValidity('请输入姓名!');
		}
	});

③禁止验证:

描述案例:当一个input的type为number,而作用是输入电话号码,其中电话号码带有“-”,提交时就会显示验证错误信息“请输入一个数字”。(这种情况下设置type可能是为了唤起特定的键盘,这里就是数字键盘)

	<form action="./api" method="post" novalidate>
		<label>电话:<input type="number" name="tel" /></label>
		<button>submit</button>
	</form>


3、表单提交

一、隐式提交:

-------如:聚焦在输入框时按回车提交表单

-------需满足以下任一条件:

                 ①表单有非禁用的提交按钮

                 ②没有提交按钮时,不超过一个类型为text、search、url、email、password、date、time、number的input元素

1、提交过程:

①根据表单enctype指定的值构建要提交的数据结构

②使用method指定的方式发送数据到action指定的目标

1.1、构建提交数据(浏览器):从可提交元素中提取数据组装成指定的数据结构的过程。

          可提交元素:button、input、keygen、object、select、textarea

1.2、编码方式(enctype)

          可指定为以下三种方式之一:①application/x-www-form-urlencoded【默认】

                                                              ②multipart/form-data

                                                              ③text/plain  (一般文件上传使用这种方式)

以上三种编码方式分别使用method="post"提交数据:

	<form action="./api" method="post">
		<input name="a" />
		<input name="b" />
		<input name="c" />
		<button>submit</button>
	</form>

enctype Content-Type数据格式组织形式
使用&分割的键值对
用回车换行分割的键值对
字节流

特殊案例

-------name="isindex" && type="text"

                    *编码方式为application/x-www-form-urlencoded

                    *作为表单的第一个提交元素

                    *提交时只发送value值,不包含name

	<form action="./api" method="post">
		<p><input name="isindex" /></p>
		<p><input name="a" /></p>
		<p><button>submit</button></p>
	</form>

提交111111、22222,则之后看到提交的数据为:111111&a=22222

-------name="_charset_" && type="hidden"

                    *没有设置value值

                    *提交时value自动用当前提交的字符集填充

	<form action="./api" method="post">
		<input type="hidden" name="_charset_" />
		<p><input name="a" /></p>
		<p><button>submit</button></p>
	</form>

提交111111,而_charset_的值就会用当前数据的编码字符集来填充,则提交的数据为:_charset_=UTF-8&a=111111

2、submit() 提交

在表单提交中的一个重要接口就是submit(),除了用提交按钮来提交数据之外,我们还可以调用form上的submit()接口来提交表单。

①onsubmit——不管用什么方式提交表单都会触发onsubmit事件。

——表单提交事件

——在这个事件触发的时候我们可以做一些提交之前的数据验证

——如果这个验证没有通过,可以阻止事件的默认行为来取消表单提交。

	form.addEventListener('submit', function(event){
		var notValid = false;
		var elements = event.target.elements;

		//TODO 处理自定义的验证规则

		if(notValid){
			event.preventDefault();
		}
	});

示例:无刷新表单提交

常用的可能是ajax,但这里用另外一种方式:

涉及到的知识点:form、target、iframe

使用iframe做中间代理,结合表单的target。

说明:指定一个iframe的name,将form的target指向iframe的name,当form表单的数据提交到服务器上,服务器返回的结果就会到iframe中,然后就只需要将iframe中的结果提交到页面上就可以了。

	<iframe name="targetFrame" class="f-hidden"></iframe>
	<form action="./api" method="post" target="targetFrame">
		<p><input name="a"></p>
		<p><input name="b" /></p>
		<p><button>submit</button></p>
	</form>


4、表单应用

当重新输入时,错误信息就会消失。

一、登录接口:

——请求地址:/api/login

——请求参数:

                *telephone     手机号码

                *password     密码,MD5加密

——返回结果:

                *code       请求状态,200表示成功

                *result      请求结果数据

登录表单:

	<form action="/api/login" class="m-form" name="loginForm" target="result" autocomplete="off">
		<legend>手机号码登录</legend>
		<fieldset>
			<div class="msg" id="message"></div>
			<div>
				<label for="telephone">手机号:</label>
				<input id="telephone" name="telephone" class="u-txt" type="tel" maxlength="11" required pattern="^0?(13[0-9]|15[012356789]|18[0236789]|14[57])[0-9]{8}$" /><br/>
				<span class="tip">11位数字手机号码</span>
			</div>
			<div>
				<label for="password">密  码:</label>
				<input id="password" name="password" type="password" class="u-txt" /><br/>
				<span class="tip">至少6位,同时包含数字和字母</span>
			</div>
			<div><button name="loginBtn">登  录</button></div>
		</fieldset>
	</form>

	var form = document.forms.loginForm;//登录表单的引用
	var nmsg = document.getElementById('message');//信息提示节点的引用

	/*控制提示消息的样式*/
	.m-form .j-err{display:block;color:#FF0000;}
	.m-form .j-suc{display:block;color:#158226;}
	/*验证出错时输入框的效果*/
	.m-form .j-error{border-color:#f00;background-color: #FFE7E7;}
	/*标示按钮的禁用状态,用在登录按钮上*/
	.m-form .j-disabled{cursor:default;background-color:#ddd;}

①通用方法:

	//在信息提示节点上设置提示信息
	function showMessage(clazz,msg){
		if(!clazz){
			nmsg.innerHTML = '';
			nmsg.classList.remove('j-suc');
			nmsg.classList.remove('j-err');
		}else{
			nmsg.innerHTML = msg;
			nmsg.classList.add(clazz);
		}
	}
	//输入验证失败的逻辑
	function invalidInput(node,msg){
		showMessage('j-err',msg);
		node.classList.add('j-error');
		node.focus();
	}
	//当用户有输入时,清除提示信息
	function clearInvalid(node){
		showMessage();
		node.classList.remove('j-error');
	}
	//提交表单以后,服务器没有返回结果的这段时间,禁用登录按钮,防止用户重复提交
	function disableSubmit(disabled){
		form.loginBtn.disabled = !!disabled;
		var method = !disabled?'remove':'add';
		form.loginBtn.classList[method]('j-disabled');
	}

②验证手机号:
	//验证手机号(使用验证逻辑)
	form.telephone.addEventListener('invalid', function(event){
		event.preventDefault();
		invalidInput(form.telephone,'请输入正确的手机号码');
	});

③验证密码:
	//验证密码,注意:验证时机是提交表单的时候,不是点击提交按钮的时候。原因:用户提交表单不一定是要点击提交按钮,还可以隐式提交
	form.addEventListener('submit', function(event){
		//密码验证
		var input = form.password,
			pswd = input.value,
			emsg = '';
		if(pswd.length<6){
			emsg = '密码长度必须大于6位';
		}else if(!/\d/.test(pswd)||!/[a-z]/i.test(pswd)){
			emsg = '密码必须包含数字和字母';
		}
		//密码验证不通过
		if(!!emsg){
			event.preventDefault();
			invalidInput(input,emsg);
			return;
		}
		//TODO 提交数据
	});

④表单提交:
	//表单提交
	form.addEventListener('submit', function(event){
		//TODO 密码验证
		input.value = md5(pswd);
		//禁用提交按钮
		disableSubmit(true);
	});

⑤状态恢复:
	//验证失败之后的状态恢复
	form.addEventListener('input', function(event){
		//还原错误状态
		clearInvalid(event.target);
		//还原登录按钮状态
		disableSubmit(false);
	});

案例介绍:利用iframe无刷新提交

	<iframe name="result" id="result" style="display:none;"></iframe>
	<form target="result" action="/api/login" class="m-form" name="loginForm">
		
	</form>

	//从iframe中取出服务器返回结果并做处理
	var frame = document.getElementById('result');
	frame.addEventListener('load', function(event){
		try{
			var result = JSON.parse(frame.contentWindow.document.body.textContent);
			//还原登录按钮状态
			disableSubmit(false);
			//识别登录结果
			if(result.code === 200){
				showMessage('j-suc','登录成功!');
				form.reset();
			}
		}catch(ex){
			//ignore
		}
	});


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值