第19章 表单脚本
第19章 表单脚本
JavaScript的原始用途之一是将一些表单处理职责转移到浏览器上,而不是依靠服务器来完成这些工作。尽管自那时起Web和JavaScript一直在发展,但是Web表单或多或少保持不变。 Web表单无法为常见问题提供直接的解决方案,导致开发人员不仅使用JavaScript进行表单验证,而且还增强标准表单控件的默认行为。
表单基础
Web表单在HTML中以<form>元素表示,在JavaScript中以HTMLFormElement类型表示。
HTMLFormElement类型继承自HTMLElement,因此同其他HTML元素一样有着同样的默认属性。但HTMLFormElement类型有如下额外的属性和方法:
acceptCharset 服务器可以处理的字符集,等效于HTML的accept-charset特性。
action 发送请求的URL,等同于HTML的action特性。
elements 表单中所有控件的一个HTMLCollection集合。
enctype 请求的编码类型,等效于HTML的enctype特性。
length 表单中控件的数量。
method 发送的HTTP请求的类型。通常为"get"或"post",等同于HTML的method特性。
reset() 重置所有表单字段为其默认值。
submit() 提交表单。
target 用于发送请求和接收响应的窗口的名称;等同于HTML的target特性。
获取<form>元素的引用有多种方式:
// 从id获取
let form = document.getElementById("form1");
// 通过索引从document.forms集合获取
let firstForm = document.forms[0];
// 通过名字从document.forms集合获取
let myForm = document.forms["form2"];
提交表单
表单提交通常使用提交按钮或图片按钮,提交按钮通常使用<input>元素或<button>元素,并将其type特性设置为"submit"。图片按钮使用<input>元素并将其type特性设置为“image”。如下:
<input type="submit" value="提交表单" />
<button type="submit">提交表单</button>
<input type="image" src="somePicture.png" />
当表单控件具有焦点时按Enter键将提交表单。
当以这种方式提交表单时,submit事件将在请求发送到服务器之前立即触发。这样就有机会验证表单数据并决定是否进行表单提交。阻止事件的默认行为会取消表单提交。如下:
let form = document.getElementById("myForm");
form.addEventListener("submit", (event) => {
event.preventDefault();
});
preventDefault()方法阻止提交表单。通常,当表单中的数据无效并且不应将其发送到服务器时,可使用此功能。
可从JavaScript调用submit()方法,以编程方式提交表单。可以随时调用此方法来提交表单,并且不需要在表单中显示“提交”按钮即可正常运行:
let form = document.getElementById("myForm");
form.submit();
当 通过submit()提交表单时,submit事件不会触发 ,因此请确保在调用该方法之前进行数据验证。
表单提交的最大问题之一是有可能多次提交表单,如用户单击“提交”按钮多次(导致服务器处理重复的请求)。有两种方法可以解决此问题:提交表单后禁用提交按钮,或使用onsubmit事件处理程序取消任何进一步的表单提交。
重置表格
当用户点击重置按钮时会重置表单,重置按钮可使用设置type特性为“reset"的<input> 或 <button>元素创建:
<input type="reset" value="重置" />
<button type="reset">重置</button>
重置表单后,所有表单字段都被设置回初次渲染页面时的值。
当用户通过单击重置按钮重置表单时,将触发reset事件。此事件有机会取消重置。如下防止表单被重置:
let form = document.getElementById("myForm");
form.addEventListener("reset", (event) => {
event.preventDefault();
});
与表单提交一样,可以通过JavaScript使用reset()方法来完成表单的重置:
let form = document.getElementById("myForm");
form.reset();
reset()会触发reset事件 ,就像单击重置按钮一样。
表单字段
可以使用原生DOM方法访问表单元素。此外,所有表单元素都是elements集合的一部分,elements也是表单的属性。 elements集合是对表单中所有表单字段的引用的有序列表,包括<input>, <textarea>,<button>, <select>和<fieldset>元素。每个表单字段在elements集合中以标记的顺序排列,并可按位置和名称进行索引:
<form method="post" id="myForm">
<textarea rows="3" cols="3" value="textarea">666</textarea>
<input type="password" name="password" value="mima666" />
<button type="submit" name="tijiao" value="按钮">提交</button>
<fieldset>fieldset</fieldset>
</form>
let form = document.getElementById("myForm");
let field1 = form.elements[0];
console.log(field1.value); //666
let field2 = form.elements["tijiao"];
console.log(field2.value); //按钮
let fieldCount = form.elements.length;
console.log(fieldCount); //4
常见的表单字段属性
除了/<fieldset/>元素外,所有的表单字段共享一些属性:
disabled 布尔值,指示字段是否被禁用。
form 指向该字段属于的表单的指针,只读。
name 字段名。
readOnly 布尔值,指示该字段是否只读。
tabIndex 指示字段的tab顺序。
<div<div<divtabindex="2">div01</div>
tabindex="1">div02</div>
tabindex="3">div03</div>
type 字段类型:复选框,单选等等。
value 将提交给服务器的字段的值。对于文件输入字段,此属性是只读的,仅包含计算机上文件的路径。
由于各个浏览器之间的计时问题,不可将onclick事件处理程序附加到“提交”按钮上:某些浏览器在表单的submit事件之前触发click事件,有些在之后。对于先触发click的浏览器,该按钮将在提交之前被禁用,这意味着该表单将永远不会提交。
对于<input>元素,其type值等同于HTML的type特性。其他元素的type值如下:
描述 | html示例 | type的值 |
---|---|---|
单选列表 | <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值为只读。
<form method="post" id="myForm">
<select></select>
<select multiple="multiple"></select>
<button></button>
<button type="reset"></button>
<button type="submit" ></button>
<input type="password"/>
</form>
let form = document.getElementById("myForm");
for (i = 0; i < form.elements.length; i++) {
console.log(form.elements[i].type);
}
// select - one
// select - multiple
// submit
// reset
// submit
// password
常见表单字段方法
每个表单字段都有两个共同的方法:focus()和blur()。 focus()方法将浏览器的焦点设置到表单字段,这意味着该字段将变为激活状态并将响应键盘事件。例如,接收焦点的文本框将显示插入符,并准备接受输入。最常使用focus()方法来引起用户对页面某些部分的注意。例如,在加载页面时,将焦点移到表单中的第一个字段是很常见的。可以通过侦听load事件,然后在第一个字段上调用focus()来完成此操作,如下所示:
window.addEventListener("load", (event) => {
document.forms[0].elements[0].focus();
});
HTML5为表单字段引入了autofocus特性,使支持该特性的浏览器无需使用JavaScript即可将焦点自动设置为该元素:
<input type="text" autofocus>
为了使先前的代码与自动对焦一起正常工作,必须首先检测是否已设置了自动对焦:
window.addEventListener("load", (event) => {
let element = document.forms[0].elements[0];
if (element.autofocus !== true) {
element.focus();
console.log("JS focus");
}
});
注意:默认情况下,只有表单元素可以将焦点设置为autofocus。可以通过将元素的tabIndex属性设置为–1,然后调用focus()来使任何元素获得焦点。唯一不支持此技术的浏览器是Opera。
与focus()相对的是blur(),它从元素中删除焦点。调用blur()时,焦点不会移到任何特定的元素上;它只是从调用它的字段中移除。在引入readonly特性之前,此方法已在Web开发的早期阶段用于创建只读字段。很少需要调用blur(),但在必要时可以使用它:
document.forms[0].elements[0].blur();
常见的表单字段事件
除了鼠标,键盘,突变和HTML事件外,所有表单字段还支持以下三个事件:
blur 当字段失去焦点时触发。
change 当<input>或<textarea>元素失去焦点且value改变时触发;<select>元素的选项改变时触发。
focus 字段获得焦点时触发。
用户手动更改字段的焦点以及调用blur()或focus()方法时blur和focus事件都会触发:
<form method="post" id="myForm">
<input type="text" />
</form>
let textbox = document.forms[0].elements[0];
textbox.addEventListener("focus", (event) => {
let target = event.target;
if (target.style.backgroundColor != "red") {
target.style.backgroundColor = "yellow";
}
});
textbox.addEventListener("blur", (event) => {
let target = event.target;
target.style.backgroundColor = /[^\d]/.test(target.value) ? "red" : "";
});
textbox.addEventListener("change", (event) => {
let target = event.target;
target.style.backgroundColor = /[^\d]/.test(target.value) ? "red" : "";
});
注意:blur和change事件之间的关系没有严格的定义。在某些浏览器中,blur事件会在change事件之前触发;有些则相反。因此需小心使用。
文本框
在HTML中有两种方式呈现文本框,单行使用<input>,多行使用<textarea>。
默认情况下,<input>元素展示为一个文本框,即使删掉type特性(默认值为“text”)。size特性指定文本框的宽度,maxlength特性指定文本框内字符的最大宽度。value特性指定文本框的初始字符。如下所示:
<input type="text" size="50" maxlength="25" value="南风知我意,吹梦到西洲">
<textarea> 元素始终渲染多行文本框,可使用rows特性指定文本框的高度(以字符大小为单位),cols特 性指定文本框的宽度。 <textarea> 的初始值需包含在标签中间:
<textarea rows="3" ,cols="5">南风知我意,吹梦到西洲。</textarea>
二者都将内容放在value属性中 :
let textbox = document.forms[0].elements[0];
console.log(textbox.value); //南风知我意,吹梦到西洲。
textbox.value = "十步杀一人,千里不留行。";
console.log(textbox.value); //十步杀一人,千里不留行。
应使用value属性读写文本框的值,而不是使用标准的DOM方法。例如:不可使用setAttribute()来设置<input>的value特性;不要尝试修改<textarea>元素的第一个子节点。
文本选择
两种文本框都支持select()方法,该方法选取文本框内所有文本。当select()方法调用时,大多数浏览器会自动将焦点设置到文本框(Opera例外)。该方法不接受参数且可以随时调用:
let textbox = document.forms[0].elements[0];
textbox.select();
在获得焦点时选择文本框中的所有文本是很普遍的,尤其是在文本框具有默认值的情况下:
let textbox = document.forms[0].elements[0];
textbox.addEventListener("focus", (event) => {
event.target.select();
});
select事件
伴随select()方法的select事件,在选择文本时触发。准确的触发时间因浏览器而异。大部分是一旦用户结束文本选择就触发:
let textbox = document.forms[0].elements[0];
textbox.addEventListener("select", (event) => {
console.log(`Text selected: ${textbox.value}`);
});
获取选择的文本
select事件不提供有关已选择什么文本的信息。 HTML5通过引入一些扩展来解决此问题,以便更好地获取选定的文本。规范将两个属性添加到文本框:selectionStart和selectionEnd。这些属性包含从零开始的数字,这些数字指示文本选择边界(分别为文本选择开始位置的偏移量和文本选择结束位置的偏移量)。因此,要在文本框中获取选定的文本,可以使用以下代码:
<form>
<textarea>南风知我意,吹梦到西洲</textarea>
</form>
let textbox = document.forms[0].elements[0];
textbox.addEventListener("select", (event) => {
console.log(`选取的文本是: ${getSelectedText(textbox)}`);
});
function getSelectedText(textbox) {
return textbox.value.substring(textbox.selectionStart,
textbox.selectionEnd);
}
部分文本选择
HTML5规范了setSelectionRange()方法,该方法接受两个参数(同substring()的参数):开始字符的索引和结束字符的索引:
//需获得焦点
<form>
<textarea autofocus="autofocus">南风知我意,吹梦到西洲</textarea>
</form>
let textbox = document.forms[0].elements[0];
textbox.setSelectionRange(0, textbox.value.length);//选中整个文本
textbox.setSelectionRange(0, 3);//选中文本前三个字符
部分文本选择对于实现高级文本输入框(例如提供自动补全建议的文本框)很有用。
输入过滤
文本框通常会期望某种类型的数据或数据格式。数据可能需要包含某些字符或必须与特定模式匹配。默认情况下文本框没有提供额外的验证方式,因此必须使用JavaScript来完成这种输入过滤。
屏蔽字符
仅允许输入数字可使用如下代码:
let textbox = document.forms[0].elements[0];
textbox.addEventListener("keypress", (event) => {
// charCode包含与所按下键相关的字符的ASCII码。
if (!/\d/.test(String.fromCharCode(event.charCode))) {
event.preventDefault();
}
});
处理剪贴版
如下六个事件与剪贴板有关:
beforecopy 复制操作发生前触发
copy 复制操作发生时触发
beforecut 剪切操作发生前触发
cut 剪切操作发生时触发
beforepaste 粘贴操作发生前触发
paste 粘贴操作发生时触发
剪贴板数据可通过存在于window对象(IE)或event对象(Firefox,Safari和Chrome)中的clipboardData对象访问。在Firefox,Safari和Chrome中,clipboardData对象仅在剪贴板事件期间可用,以防止未经授权的剪贴板访问; IE始终显示clipboardData对象。为了实现跨浏览器的兼容性,最好仅在剪贴板事件期间使用此对象。
clipboardData对象上有三种方法:getData(),setData()和clearData()。 getData()方法从剪贴板中获取字符串数据,并接受单个参数,该参数是要获取的数据的格式。 IE指定两个选项:“text”和“URL”。Firefox,Safari和Chrome浏览器期望使用MIME类型,但也接受“text”,等同于“text/plain”。
setData()方法类似:其第一个参数是数据类型,第二个参数是放置在剪贴板上的文本。 IE支持“text”和“URL”,然而Safari和Chrome需要MIME类型。与getData()不同,Safari和Chrome无法识别“text”类型。可使用如下跨浏览器方法:
function getClipboardText(event) {
var clipboardData = (event.clipboardData || window.clipboardData);
return clipboardData.getData("text");
}
function setClipboardText(event, value) {
if (event.clipboardData) {
return event.clipboardData.setData("text/plain", value);
} else if (window.clipboardData) {
return window.clipboardData.setData("text", value);
}
}
在paste事件中,可以确定剪贴板上的文本是否无效,如果无效,则取消其默认行为,如以下示例所示:
textbox.addEventListener("paste", (event) => {
let text = getClipboardText(event);
if (!/^\d*$/.test(text)) {
event.preventDefault();
}
});
自动前进标签
JavaScript可通过多 种方式用于增加表单字段的可用性。最常见的一种方法是在当前字段完成后自动将焦点移到下一个字段。如输入电话号码:
<input type="text" name="tel1" id="txtTel1" maxlength="4">
<input type="text" name="tel2" id="txtTel2" maxlength="11">
function tabForward(event) {
let target = event.target;
if (target.value.length == target.maxLength) {
let form = target.form;
for (let i = 0, len = form.elements.length; i < len; i++) {
if (form.elements[i] == target) {
if (form.elements[i + 1]) {
form.elements[i + 1].focus();
}
return;
}
}
}
}
let inputIds = ["txtTel1", "txtTel2"];
for (let id of inputIds) {
let textbox = document.getElementById(id);
textbox.addEventListener("keyup", tabForward);
}
let textbox1 = document.getElementById("txtTel1");
let textbox2 = document.getElementById("txtTel2");
HTML5约束验证API
HTML5引入了浏览器在提交给服务器之前验证表单中数据的功能。即使JavaScript不可用或无法加载,此功能也可以进行基本的验证。浏览器本身根据代码中的规则执行验证,然后自行显示适当的错误消息(不需要其他JavaScript)。此功能仅在支持的浏览器中起作用,包括所有现代浏览器(Safari除外)和IE 10+。
验证仅在某些条件下才应用于表单字段。可以使用HTML标记在特定字段上指定约束,这将导致浏览器自动执行表单验证。
必填字段
当一个字段有required特性约束时:
<input type="text" name="username" required>
标记为required的任何字段都必须要求有一个值,以便提交表单。该特性仅适用于<input>、<textarea>和 <select>字段。
可选输入类型
HTML5为<input>元素上的type特性指定了几个附加值。这些类型特性不仅提供有关预期数据类型的额外信息,还提供一些默认验证。最广泛支持的两种新输入类型是“email”和“URL”,每种类型都带有浏览器适用的自定义验证:
<input type="email" name="email">
<input type="url" name="homepage">
数值范围
HTML5还提供了input元素的"number"、"range"、"datetime"、"datetime-local"、"date"、"month"、 "week"和"time"类型,并不是所有浏览器都支持。如下:
<input type="number" min="0" max="100" step="5" name="count">
input.stepUp(); // 加1
input.stepUp(5); // 加5
input.stepDown(); // 减1
input.stepDown(10); // 减10
输入模式
在HTML5中为文本字段引入了pattern特性。该特性指定输入值必须与正则表达式匹配。例如,要在文本字段中仅允许数字,以下代码将应用此约束:
<input type="text" pattern="\d+" name="count">
可通过访问pattern属性来读取模式:
let pattern = document.forms[0].elements["count"].pattern;
测试浏览器是否支持pattern特性:
let isPatternSupported = "pattern" in document.createElement("input");
检测有效性
可以使用checkValidity()方法检查表单上的字段是否有效。所有元素均提供此方法,如果该字段的值有效,则返回true,否则返回false。字段是否有效取决于本节前面提到的条件,因此,没有值的必填字段被视为无效,其值与pattern特性不匹配的字段被视为无效。例如:
if (document.forms[0].elements[0].checkValidity()){
// 字段有效
} else {
// 字段无效
}
要检查整个表单是否有效,可以在表单自身使用checkValidity()方法。如果所有表单字段均有效,则此方法返回true,如果有一个表单字段无效,则返回false:
if (document.forms[0].checkValidity()){
// 表单有效
} else {
// 表单无效
}
然而checkValidity()只是告知一个字段是否有效,而validity属性则准确指示了该字段为何有效或无效。该对象具有一系列返回布尔值的属性:
- customError 设置了setCustomValidity()为true,否则为false
document.forms[0].elements[0].setCustomValidity("干的漂亮");
//console.log(document.forms[0].elements[0].validity);
效果如下:
patternMismatch 如果值与指定的pattern特性不匹配,则为true
rangeOverflow 值比max大为true
rangeUnderflow 值比min小为true
stepMisMatch 值在min和max范围内未与step特性匹配,则为true
tooLong 如果该值包含的字符超过maxlength属性所允许的字符,则为true。某些浏览器(例如Firefox4 )会自动限制字符数,因此此值可能始终为false。
tooShort 如果该值包含的字符小于minlength属性所允许的字符,则为true。
typeMismatch 值不是要求的“email”或“url”格式,则为true
valid 元素满足所有的验证约束,则为true
valueMissing 如果字段被标记为必填且没有任何值,则为true。
禁用验证
可以通过指定novalidate特性来指示表单不要对其进行任何验证:
<form novalidate="novalidate"></form>
也可以使用JavaScript属性noValidate获取或设置此值:
document.forms[0].noValidate = true;
如果表单中有多个提交按钮,则可以通过将formnovalidate特性添加到指定按钮使其不进行验证:
<form action="" method="post">
<input type="submit" value="常规提交">
<input type="submit" formnovalidate name="btnNoValidate" value="不验证提交" />
</form>
选择框
选择框使用<select>和<option>元素创建,为了便于与控件进行交互,HTMLSelectElement类型除了在所有表单字段上可用的属性和方法之外,还提供了以下属性和方法:
- add(newOption, relOption) 在关联选项之前添加一个新的<option>元素
<select id="slp" size="2">
<option id="ganyu">甘雨</option>
<option id="keqing" value="kq">刻晴</option>
</select>
<button onclick="selctInfo()">选择框信息</button>
let newOption = document.createElement("option");
newOption.id = "hutao";
let text01 = document.createTextNode("胡桃");
newOption.appendChild(text01);
let relOption = document.getElementById("keqing");
document.getElementById("slp").add(newOption, relOption);
let op = document.getElementById("slp");
console.log(op.multiple); //true
console.log(op.options); // HTMLOptionsCollection(3)
function selctInfo() {
console.log(`选中的项的索引是:${op.selectedIndex}`);
console.log(op.size);
}
multiple 一个布尔值,指示是否允许选择多个项;否则为false。等同于HTML的Multiple特性。
options 控件中所有<option>元素的一个HTMLCollection集合
remove(index) 移除给定位置的<option>元素
selectedIndex 所选选项的从零开始的索引;如果未选择任何选项,则为–1。对于允许多个选择的选择框,则是第一个选中的项的索引。
size 在选择框中可见的行数;等效于HTML的size特性。
选择框的type属性是“select-one”或“select-multiple”,具体取决于multiple特性。当前选择的项根据以下规则确定选择框的value属性:
如果没有选择任何项,则选择框的值是空字符串
如果选择了一个选项,并且指定了value特性,则选择框的值就是所选选项的value特性。即使value特性
为空字符串也是如此。
如果选择了一个选项,但未指定value特性,则选择框的值就是该选项的文本。
如果选择了多个选项,则选择框的值将根据前两个规则从第一个选择的选项中获取。
每个<option>元素在DOM中由HTMLOptionElement对象表示。 HTMLOptionElement类型添加以下属性,以简化数据访问:
index options集合中的选项索引。
label 选项的标签,等效于HTML的label特性。
selected 一个布尔值,用于指示是否选择了该选项。将此属性设置为true以选择该选项。
text 选项的文本
value 选项的值(等同于HTML的value特性)
其他表单字段在值更改且失去焦点后触发change事件;而选中选项后,change事件就会在选择框上触发。
注意:value属性在不同浏览器中返回的内容有所不同。在所有浏览器中,value属性始终等于value特性。如果未指定value特性,则IE8和更早版本将返回一个空字符串,而IE9+,Safari,Firefox,Chrome和Opera将返回与文本相同的值。
选项选择
对于只允许选择一个选项的选择框,访问所选选项的最简单方法是使用选择框的selectedIndex属性获取该选项,如以下示例所示:
let selectedOption = selectbox.options[selectbox.selectedIndex];
移除选项
移除单个选项:
selectbox.removeChild(selectbox.options[0]);
selectbox.remove(0);
selectbox.options[0] = null;
移除全部选项:
function clearSelectbox(selectbox) {
for (let option of selectbox.options) {
selectbox.remove(0);
}
}
移动和重排选项
将第一个选项从一个选择框移到另一个选择框:
let selectbox1 = document.getElementById("selLocations1");
let selectbox2 = document.getElementById("selLocations2");
selectbox2.appendChild(selectbox1.options[0]);
移动选项与删除选项一样,每个选项的index属性都将重置。
将一个选项上移一个位置:
let optionToMove = selectbox.options[1];
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index - 1]);
表单序列化
随着Ajax的出现(后面讨论),表单序列化已成为常见的要求。可以使用表单字段的type属性以及name和value属性在JavaScript中序列化表单。
在编写代码之前,需要了解浏览器如何确定表单提交期间发送到服务器的内容:
字段名称和值使用URL编码,并使用&符号定界。
不发送已禁用的字段。
复选框或单选框仅在选中时才发送。
不会发送“reset”或“button”类型的按钮。
多选字段有每个选中的值的记录(entry)。
通过单击提交按钮提交表单时,将发送该提交按钮;类型为“image”的<input>元素也视为提交按钮。
<select>元素的值是选中的<option>元素的value特性;如果该<option>元素没有value特性,则为文本的值。
表单序列化通常不包含任何按钮字段,因为结果字符串很可能会以其他方式提交。应当遵守所有其他规则。完成表单序列化的代码如下:
function serialize(form) {
let parts = [];
let optValue;
for (let field of form.elements) {
switch (field.type) {
case "select-one":
case "select-multiple":
if (field.name.length) {
for (let option of field.options) {
if (option.selected) {
if (option.hasAttribute) {
optValue = (option.hasAttribute("value") ?
option.value : option.text);
} else {
optValue = (option.attributes["value"].specified ?
option.value : option.text);
}
parts.push(encodeURIComponent(field.name) + "=" +
encodeURIComponent(optValue));
}
}
}
break;
case undefined: // fieldset
case "file": // file input
case "submit": // submit button
case "reset": // reset button
case "button": // custom button
break;
case "radio": // radio button
case "checkbox": // checkbox
if (!field.checked) {
break;
}
default:
// don't include form fields without names
if (field.name.length) {
parts.push('${encodeURIComponent(field.name)}=' +
'${encodeURIComponent(field.value)}');
}
}
return parts.join("&");
}
}
serialize()函数首先定义一个称为part的数组,以保存接下来要创建的字符串的各部分。然后,一个for循环遍历每个表单字段,并将其存储在field变量中。一旦获得了字段引用,就使用switch语句检查其类型。通过遍历控件中的所有选项并添加一个值(如果选择了该选项)来完成序列化。对于单选控件,只会选择一个选项,而多选控件可能会选择零个或多个选项。两种选择类型都可以使用相同的代码,因为对选择数量的限制是由浏览器强制执行的。选择一个选项后,需要确定要使用哪个值。如果不存在value特性,则应使用文本,带有空字符串的value特性是完全有效的。要对此进行检查,需要在符合DOM的浏览器中使用hasAttribute(),并在I8及更早版本中使用该特性的specified属性。
如果<fieldset>元素在表单中,虽出现在elements集合中但其没有type属性。因此,如果type是undefined,则无需序列化。所有类型的按钮和文件输入字段都适用。(文件输入字段包含表单提交中文件的内容;但是,无法模仿这些字段,因此在序列化中通常将其省略。)对于单选和复选框控件,将检视checked属性,如果为false,则退出switch语句。如果checked为true,则代码将继续在default语句中执行,该语句对字段的名称和值进行编码,并将其添加到parts数组中。注意,在所有情况下,没有名称的表单字段都不会作为模拟浏览器表单提交行为的序列化的一部分而包括在内。该函数的最后一部分使用join()来正确设置字符串的格式,并在各字段之间使用&号。
富文本编辑
Web应用程序最需要的功能之一是能够编辑网页上的富文本(也称为“所见即所得”编辑)。基本原理是在页面中嵌入包含空白HTML文件的iframe。通过designMode属性,可以使该空白文档可编辑,此时,您正在编辑页面中<body>元素的HTML。designMode属性具有两个可能的值:“ off”(默认值)和“ on”。设置为“on”时,整个文档将变为可编辑状态(显示脱字符),可以像使用文字处理程序一样编辑文本,通过键盘将文本标记为粗体、斜体,等等。
一个空白HTML页面用作iframe的源:
<!DOCTYPE html>
<html>
<head>
<title>用于富文本编辑的空白页</title>
</head>
<body>
</body>
</html>
此页面将被加载到iframe中。要对其进行编辑,必须将designMode设置为“on”,但只有在文档完全加载后才可以。需使用onload事件处理程序来指示设置designMode的适当时间,如以下示例所示:
<iframe name="richedit" style="height: 100px; width: 100px"></iframe>
window.addEventListener("load", () => {
frames["richedit"].document.designMode = "on";
});
使用contenteditable
与富文本进行交互的另一种方法(也是由IE首次实现)是通过使用contenteditable特性实现。contenteditable特性可以应用于页面上的任何元素,并立即使该元素变成可编辑状态。这种方法之所以受到青睐,是因为它不需要iframe,空白页和JavaScript的开销。仅需将特性添加到元素:
<div class="editable" id="richedit" contenteditable>南风知我意</div>
已包含在元素中的文本将自动变为可编辑状态,类似于<textarea>元素。还可以通过在元素上设置contentEditable属性来打开或关闭编辑模式:
let div = document.getElementById("richedit");
richedit.contentEditable = "true";
contentEditable有三个可能的值:“true”打开,“false”关闭,或“继承”从父级继承设置。IE,Firefox,Chrome,Safari和Opera以及所有主要的移动浏览器均支持contentEditable特性。
与富文本交互
与富文本编辑器进行交互的主要方法是使用document.execCommand()(该方法已废弃)。此方法在文档上执行命名命令(named commands),可用于大多数格式更改。 document.execCommand()有三个可能的参数:
- 要执行的命令的名称;
- 一个指示浏览器是否应为该命令提供用户界面的布尔值;
- 以及该命令正常工作所必需的值( 没必要的话为null)。
为了跨浏览器兼容性。第二个参数应始终为false,因为传入true时Firefox会引发错误。
每个浏览器都支持不同的命令集。下表列出了最常用的命令:
命令 | 值(第三个参数) | 描述 |
---|---|---|
backcolor | 一个颜色的字符串 | 设置文档的背景色 |
bold | null | 将选择的文本变粗体 |
copy | null | 在文本选取进行剪切板复制 |
createlink | 一个URL字符串 | 将当前的文本选区变为指向给定URL的链接。 |
cut | null | 对文本选区执行剪切板剪切 |
delete | null | 删除当前选区文本 |
fontnane | 字体名 | 使用给定的字体改变选区文本 |
fontsize | 1到7 | 改变选区文本字体大小 |
forecolor | 一个颜色字符串 | 改变选区文本的颜色 |
formatblock | html标签,如<h1> | 使用特定的HTML标签对文本选区外整个文本框进行格式化。 |
indent | null | 缩进文本 |
inserthorizontalrule | null | 在脱字符位置插入 <hr> 元素 |
insertimage | 图片的URL | 在脱字符位置插入图片 |
insertorderedlist | null | 在脱字符位置插入 <ol> |
insertunorderedlist | null | 在脱字符位置插入 <ul> |
insertparagraph | null | 在脱字符位置插入 <p> |
italic | null | 切换选区文本为斜体 |
富文本选区
可以使用iframe的getSelection()方法在富文本编辑器中获取选区。此方法在document对象和window对象上均可用,并返回表示当前所选文本的Selection对象。每个Selection对象具有以下属性:
anchorNode 选区开始的节点
anchorOffset 选区之前在anchorNode中被跳过的字符数
focusNode 选区结束的节点
focusOffset focusNode中未包含选区的字符数
isCollapsed 布尔值,指示选区的开始和结束是否相同
rangeCount 选区中DOM范围的数量
Selection的属性不包含一些有用的信息。但下面方法可以:
addRange(range) 将给定的DOM范围添加到选区
collapse(node, offset) 在给定节点内的给定文本偏移量处折叠选区
collapseToEnd() 折叠选区到末尾
collapseToStart() 折叠选区到开头
containsNode(node ) 确定给定的节点是否包含在选区中
deleteFromDocument() 从文档中删除选区
extend(node, offset) 通过移动focusNode和focusOffset到指定值扩展选区
getRangeAt(index) 返回选区中给定索引处的DOM范围
removeAllRanges() 从选区中删除所有DOM范围。由于选区中至少有一个范围,因此可以有效地删除选区
removeRange(range 从选区中删除指定的DOM范围
selectAllChildren(node) 清除选区,然后选择给定节点的所有子节点。
toString() 返回选区的文本内容
Selection对象的方法非常强大,且广泛使用DOM范围来管理选区。通过访问DOM范围,可以比使用execCommand()更加详细地修改富文本编辑器的内容,因为可以直接操纵所选文本的DOM:
<iframe name="richedit" style="height: 100px; width: 100px" >666</iframe>
<button onclick="test()">点击测试</button>
window.addEventListener("load", () => {
frames["richedit"].document.designMode = "on";
});
function test(){
let selection = frames["richedit"].getSelection();
let selectedText = selection.toString();
console.log(selectedText);
// get the range representing the selection
let range = selection.getRangeAt(0);
console.log(range);
// highlight the selected text
let span = frames["richedit"].document.createElement("span");
span.style.backgroundColor = "yellow";
range.surroundContents(span);
}
表单中的富文本
由于富文本编辑是使用iframe或contenteditable元素而不是表单控件实现的,因此,富文本编辑器从技术上讲不是表单的一部分。这意味着HTML不会提交给服务器,除非手动提取HTML并自己提交。