14 表单脚本
14.1 表单的基础知识
表单是由form元素来表示的,而js中,表单对应的则是HTMLFormElement类型(继承HTMLElement),私有属性和方法:
- acceptCharset:服务器能够处理的字符集
- action:接收的URL;
- elements:表单中所有控件的集合
- enctype:请求的编码类型
- length:表单控件中的数量
- method:要发送的HTTP请求类型
- name:表单的名称
- reset():将所有表单域重置为默认值
- submit():提交表单
- tartget():用于发送请求和接收响应的窗口名称;
14.1.1 提交表单
用户单击提交按钮或图像按钮时,就会提交表单.
//通用提交按钮
<input type="submit" value="Submit Form"></input>
//自定义提交按钮
<button type="submit">Submit Form</button>
//图像按钮
<input type="image" src="graphic.gif"></input>
var from=documen.getElementById("myForm");
EventUtil.addHandle(form,"submit",function(event){
//取得事件对象
event=EventUtil.getEvent(event);
//阻止默认事件
EventUtil.preventDefault(event);
})
from.submit();
重复提交表单解决方案:
- 第一次提交表单后就禁止提交按钮
- 利用onsubmit事件处理程序取消后续的表单提交操作
14.1.2 重置表单
用户单击重置按钮时,表单会被重置.使用type特性值为"reset"的input或button都可以创建重置按钮
//通用重置按钮
<input type="reset" value="Reset Form"/>
//自定义重置按钮
<button type="reset">Reset Form</button>
var from=documen.getElementById("myForm");
EventUtil.addHandle(form,"reset",function(event){
//取得事件对象
event=EventUtil.getEvent(event);
//阻止默认事件
EventUtil.preventDefault(event);
})
from.reset();
14.1.3 表单字段
每一个表单都有elements属性,该属性是表单中所有表单元素的集合.
var form = document.getElementById("form");
//取得表单中第一个字段
form.elements[0];
//取得表单中名称是"name"的字段
form.elements["name"];
//取得表单中包含字段的数量
form.elements.length;
如果使用的一个name,那么返回的是一个NodeList;
1.共有的表单字段属性
表单字段公有的属性如下:
- disable:布尔值,表示当前字段是否被禁用
- form:只想当前字段所属表单的指针,只读;
- name:当前字段的名称
- readOnly:布尔值,表示当前字段是否只读
- tabIndex:表示当前字段的切换序号
- value:当前字段将被提交给服务器的值.
动态修改表单属性:
var form = document.getElementById("form");
var field = form.elements[0];
//修改value属性
field.value = "456";
//检查form属性的值
console.log(field.form === form); //true
//把焦点设置到当前字段
field.focus();
//禁用当前字段
field.disabled = true;
//修改type属性(不推荐,但对input来说是可行的)
field.type = "checkbox";
能够动态修改表单字段属性,意味着我们可以在任何时候,以任何方式来动态操作表单.
2.共有的表单字段方法
- 每个表单字段都有两个方法:focus()和blur();
- html5添加了一个autofocus属性
3.共有的表单字段事件
除了支持鼠标,键盘,更改和html事件之外,所有表单字段都支持下列3个事件: - blur:当前字段失去焦点
- change:对于input和textarea元素,在他们失去焦点且value值改变时触发;对于select元素,在其选项改变时触发
- focus:当前字段获取焦点时触发
14.2 文本框脚本
input:单行文本,通过size特性,可以指定文本框能够显示的字符数.通过value特性,可以设置文本框的初始值,而maxlength特性则用于指定文本框可以接受的最大字符数
textarea:多行文本,指定文本框大小可以使用rows和clos特性.不能指定最大字符
<input type="text" size="20" value="456" maxlength="10" />
<textarea rows="20" clos="20"></textarea>
14.2.1 选择文本
上述两个文本框都支持select()方法,这个方法用于选择文本框中的所有文本.
1.选择(select)事件
IE9+ 、Safair 、Opera、Chorme、Firefox 当用户选择文件时,鼠标松开时触发
IE8以及其下 当用户选择文本时,就会触发
window.onload = function() {
if (document.addEventListener) {
document.forms[0].elements[0].addEventListener("select", function () {
console.log("select" + this.value);
}, false)
} else {
document.forms[0].elements[0].attachEvent("onselect", function (event) {
console.log("ie select" + event.srcElement.value);
});
}
}
2.取得选择的文本
H5 添加的两个属性,
selectionStart、selectionEnd
IE9+ 、Safair 、Opera、Chorme、Firefox 支持这两个属性
IE8不支持这两个属性,但是提供 document.selection 对象, 其中保存着用户在整个文档范围内选择的文本信息;也就是说,无法确定用户选择的是页面中那个部位的文本。不过,在于select事件一起使用时候,可以假设是用虎选择了文本框中的文本,因此触发该事件。要取得选择的文本,首先必须创建一个范围,然后再将文本从其中取出来,如下。
function getSelectedText(textbox){
if(typeof textbox.selectionStart == "number"){
return textbox.value.substr(textbox.selectionStart,textbox.selectionEnd);
}else if(document.selection){
return document.selection.createRange().text;
}
}
3.选择部分内容
setSelectionRange(start,end) 接收两个参数,并且不包括 end指定的内容
IE9+ 、Safair 、Opera、Chorme、Firefox
document.forms[0].elements[0].setSelectionRange(0,3); //123
document.forms[0].elements[0].setSelectionRange(0,2);//12
document.forms[0].elements[0].setSelectionRange(0,1);//1
E8以及其低版本中不支持 setSelctionRange方法,但是要想选择部分内容步骤如下:
1.createTextRange 创建一个范围,并将其放在恰当的位置
2.再通过 moveStart() 和 moveEnd()这两个范围方法将范围移动到位。
3.调用moveStart、moveEnd之前必须使用 collapse()将范围折叠到文本框的开始位置。(此时在moveStart()将范围的起始点和终点移到了相同的位置 )
4.接着再给moveEnd()传入要选择的字符总数即可。 最后一步就是使用范围的select()选择文本
跨浏览器的方式:
function selectText(textbox,start,end){
if(textbox.setSelectionRange){
textbox.setSelectionRange(start,end)
}else{
var range = textbox.createTextRange();
range.collapse(true);
range.moveStart("character",start); // character 字符 || word 单词 || sentence 段落
range.moveStart("character",end-start)
range.select();
}
}
14.2.2 过滤输入
1.屏蔽字符
使用keypress事件,可以提前每一个文本输入做验证
element.addEventListener("keypress", function (e) {
//验证操作
})
2.操作剪贴板
HTML5增加了剪切板事件:
- beforecopy:发生复制操作时触发
- copy:发生剪切操作前触发
- beforecut:在发生剪切操作前触发
- cut:在发生剪切操作时触发
- beforepaste:在发生粘贴操作前触发
- paste:在发生粘贴操作前触发
获取或设置剪贴板数据
操作的数据放在clipboardData对象中的,在ie中这个对象通过window.clipboardData来访问,在其它浏览器中则通过事件处理函数的参数来访问。
clipboardData对象有三个方法:getData()、 setData() 和 clearData()。在使用getData或setData时,要指定文档类型,ie中使用text或URL,其它浏览使用mime类型(text/plain)
setData()方法只有在ie中才能设置剪贴板数据,在其它浏览器中设置后剪贴板数据依然无效。
第三方库
使用clipboard.js进行操作,库介绍
14.2.3 自动切换焦点
为增加易用性,通四海加快数据输入,可以在前一个文本框中的字符达到最大数量后,自动将焦点切换到下一个文本框.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>test javascript</title>
<script type="text/javascript">
window.onload = function () {
var EventUtil = {
addhandler: function (element, type, handler) {
if (element.addEventListenter) {
element.addEventListenter(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
getEvent: function (event) {
return event ? event : window.event;
},
getTarget: function () {
return event.target || event.srcElement;
},
preventDefault: function () {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
stopPropagation: function () {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBuddle = true;
}
},
removehandler: function (element, type, handler) {
if (element.removeEventListenter) {
element.addEventListenter(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
getCharCode: function (event) {
if (typeof event.charCode == "number") {
return event.charCode;
} else {
return event.keyCode;
}
},
getClipboardText: function (event) {
var clipboardData = event.clipboardData || window.clipboardData;
return clipboardData.getData("text");
},
setClipboardText: function (event, value) {
if (event.clipboardData) {
return event.clipboardData.setData("text/plain", value);
} else if (window.clipboardData) {
return window.clipboardData.setData("text", value);
}
},
};
var textarea = document.forms[0].elements["text"];
var button = document.getElementById("button");
(function () {
function tabForward(event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
if (target.value.length == target.maxLength) {
var form = target.parentElement;
for (var i = 0, len = form.elements.length; i < len - 1; i++) {
if (form.elements[i] == target) {
form.elements[i + 1].focus();
return;
}
}
}
}
var textTel1 = document.getElementById("txtTel1");
var textTel2 = document.getElementById("txtTel2");
var textTel3 = document.getElementById("txtTel3");
EventUtil.addhandler(textTel1, "keyup", tabForward);
EventUtil.addhandler(textTel2, "keyup", tabForward);
EventUtil.addhandler(textTel3, "keyup", tabForward);
})();
};
</script>
</head>
<body>
<form>
<input type="text" name="tel1" id="txtTel1" maxlength="3" />
<input type="text" name="tel2" id="txtTel2" maxlength="3" />
<input type="text" name="tel3" id="txtTel3" maxlength="3" />
</form>
</body>
</html>
14.2.4 HTML5约束验证API
为了将表单提交到服务器之前验证数据,HTML5新增了一些功能.
1.必填字段
任何标注有required的字段,在提交表单时都不能空着;
<input type="text" name="user" required />
2.其他输入类型
<input type="email" name="email" />
<input type="url" name="homepage" />
3.数值范围
基于数字的值:number,range,datetime,datetime-local,date,month,week,还有time.浏览器对这几个类型的支持情况并不好.
<input type="number" min="0" max="100" name="count" />
4.输入模式
<input type="text" pattern="\d+" />
5.检测有效性
使用checkValidty()方可以检测表单中的某个字段是否有效.
if(document.forms[0].elements[0].checkValidity()){
//字段有效
}else{
//字段无效
}
检测整个表单是否有效
if(document.forms[0].checkValidity()){
//字段有效
}else{
//字段无效
}
6.禁用验证
通过设置novalidate属性,可以告诉表单不进行验证;
14.3 选择框脚本
选择框是通过select和option元素所创建的;
14.4 表单序列化
浏览器是怎样将数据发送给服务器的:
1.对表单字段的名称和值进行url编码,使用和号(&)分隔
2.不发送禁用的表单字段
3.只发送勾选的复选框和单选按钮
4.不发送type为reset和button的按钮
5.多选选择框中的每一个选中的值单独一个条目
6.在单击提交按钮提交表单的情况下,也会发送提交按钮
7.select元素的值,就是选中的option元素的value特性的值
function serializeForm2(form) {
var parts = [];
for (var i = 0, i1 = form.elements.length; i < i1; i++) {
var field = form.elements[i];
switch (field.type) {
case 'select-one':
case 'select-multiple':
if (field.type.length) {
for (var j = 0, j1 = field.options.length; j < j1; j++) {
var option = field.options[j];
if (option.selected) {
var optionValue = '';
if (option.hasAttribute('value') && option.attributes['value'].specified) {
//specified表明是否有此属性,有的话返回true,若定义了此属性但尚未添加到元素中也返回true。
optionValue = option.value;
} else {
optionValue = optionValue.text;
}
parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(optionValue));
}
}
}
break;
case undefined:
case 'file':
case 'submit':
case 'reset':
case 'button':
break;
case 'radio':
case 'checkbox':
if(!field.checked){
break;
}else{
parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.dataset['index']));
break;
}
default:
if(field.name.length){
parts.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value));
}
}
}
return parts.join('&');
}
// 调用
var oForm = document.getElementById('target');
console.log(serializeForm2(oForm));
14.5 富文本编辑器
14.5.1 实现方式
1.使用iframe实现,主要通过设置文档的designMode设置为"on":
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<iframe
src="./blank.html"
frameborder="0"
name="richedi"
style="height: 100px; width: 100px"
></iframe>
</body>
</html>
<script>
frames["richedi"].addEventListener("load", function () {
frames["richedi"].document.designMode = "on";
});
</script>
2.使用contenteditable属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.editable {
height: 100px;
width: 100px;
border: 2px solid red;
overflow-y: scroll;
}
</style>
</head>
<body>
<div class="editable" id="richedit" contenteditable=""></div>
</body>
</html>
14.5.2 操作富文本
14.5.3 富文本选区
selection 对象拥有下列属性:
- anchorNode: 选区起点所在节点
- anchorOffset:选区起点在其所在节点中的偏移量
- focusNode:选区终点所在节点
- focusOffest:选区终点在其所在节点中的偏移量
- isCollapsed:布尔值,表示选区的起点终点是否重合
- rangeCount:选区包含DOM节点的数量
拥有下列方法:
- addRange(range):将指定范围内的内容添加到选区
- collapse(node,offset):将选区折叠到指定节点的指定偏移处
- collapseToEnd():将选区折叠到选区终点的位置
- collapseToStart():将选区折叠到起点位置
- containsNode(node):确定指定节点是否包含在选区之内
- deleteFromDocument():从文档中删除选区中的文本
- extend(node,offset):将 focusNode 和 focusOffset 移动到指定位置来拓展选区
- getRangeAt(index): 返回所应对应选区中的DOM范围
- removeAllRanges():从选区中移除所有的DOM范围,该方法也会移除选区,因为选区中至少要有一个范围
- removeRange(range): 从选区中移除指定范围
- selectAllChildren(node):清除选区并选择指定节点的所有子节点
- toString():返回选区包含的指定文本
let selection = frames["richedit"].getSelection();
// 取得选择的文本
let selectedText = selection.toString();
// 取得选区范围
let range = selection.getRangeAt(0);
// 突出显示选择的文本
let span = frames["richedit"].document.createElement('span');
span.style.backgroundColor = "yellow";
range.surroundContents(span);
14.5.4 表单与富文本
由于富文本编辑器不是使用表单控件实现的,因此从技术上来说富文本编辑器并不属于表单的一部分
所以我们在提交表单时富文本编辑器内的内容并不会随着一起提交,所以我们需要手工提取并提交富文本编辑器中的内容
let form = document.forms[0];
form.onsubmit = function(event){
let target = event.target;
target.elements["comments"].value = document.getElementById("richedit").innerHtml;
}