本章內(nèi)容
- 理解表單
- 文本框驗證與交互
- 使用其他表單控制
14.1 表單的基礎知識
通過document.forms
可以取得頁面中所有的表單蜗顽。在這個集合里溺忧,可以通過數(shù)值索引或 name 值來取得特定的表單。
var firstForm = document.forms[0];
var myForm = document.forms["form2"];
14.1.1 提交表單
var form = document.getElementById("myForm");
EventUtil.addHandler(form, "submit", function(event) {
//取得事件對象
event = EventUtil.getEvent(event);
//阻止默認事件
EventUtil.preventDefault(event);
});
14.1.2 重置表單
var form = document.getElementById("myForm");
EventUtil.addHandler(form, "reset", function(event) {
//取得事件對象
event = EventUtil.getEvent(event);
//阻止表單重置
EventUtil.preventDefault(event);
});
14.1.3 表單字段
可以像訪問頁面中的其他元素一樣敏弃,使用原生 DOM 方法訪問表單元素卦羡。
- 共有的表單字段屬性
- 共有的表單字段方法
- 共有的表單字段事件
14.2 文本框腳本
在 HTML 中,有兩種方式來表現(xiàn)文本框:一種是使用<input>
元素的單行文本框麦到,另一種是使用<textarea>
的多行文本框绿饵。
14.2.1 選擇文本
上述兩種文本都支持select()
方法,這個方法用于選擇文本框中的所有文本瓶颠。
- 選擇(select)事件
- 取得選擇的文本
- 選擇部分文本
14.2.2 過濾輸入
- 屏蔽字符
響應向文本框中插入字符操作的是keypress
事件拟赊。因此,可以通過阻止這個事件的默認行為來屏蔽此類字符粹淋。
EventUtil.addHandler(textbox, "keypress", function(event) {
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);
});
運行以上代碼后吸祟,由于所有按鍵操作都將被屏蔽。如果只想屏蔽特定的字符桃移,則需要檢測keypress
事件對應的字符編碼屋匕,然后再決定如何響應。例如借杰,下列代碼只允許用戶輸入數(shù)值过吻。
EventUtil.addHandler(textbox, "keypress", function (event) {
event = EventUtil.getTarget(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if(!/\d/.test(String.fromCharCode(charCode))) {
EventUtil.preventDefault(event);
}
});
雖然理論上只應該在用戶按下字符鍵時才觸發(fā)keypress
事件,但有些瀏覽器也會對其他鍵觸發(fā)此事件蔗衡。為了讓代碼更通用纤虽,只要不屏蔽那些字符編碼小于 10 的鍵即可乳绕。將上面的函數(shù)重寫如下。
EventUtil.addHandler(textbox, "keypress", function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9) {
EventUtil.preventDefault(event);
}
});
復制逼纸、粘貼及其他操作還要用到 Ctrl 鍵恕沫。在除 IE 之外的所有瀏覽器中环疼,前面的代碼也會屏蔽 Ctrl+C蹈丸、Ctrl+V懈贺,以及其他使用 Ctrl 的組合鍵渔扎。因此井氢,最后還要添加一個檢測條件瘪板。
EventUtil.addHandler(textbox, "keypress", function(event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
if(!/\d/.test(String.fromCharCode(charCode) && charCode > 9 && !event.ctrlKey) {
EventUtil.preventDefault(event);
});
});
- 操作剪貼板
var EventUtil = {
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);
}
},
};
在paste
事件中捂龄,可以確定剪貼板中的值是否有效淑仆。
EventUtil.addHandler(textbox, "paste", function (event) {
event = EventUtil.getEvent(event);
var text = EventUtil.getClipboardText(event);
if (!/^\d*$/.test(text)) {
EventUtil.preventDefault(event);
}
});
14.2.3 自動切換焦點
(function () {
function tabForward(event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
if (target.value.length == target.maxLength) {
var form = target.form;
for(var 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;
}
}
}
}
var textbox1 = document.getElementById("txtTel1");
var textbox2 = document.getElementById("txtTel2");
var textbox3 = document.getElementById("txtTel3");
EventUtil.addHandler(textbox, "keyup", tabForward);
EventUtil.addHandler(textbox2, "keyup", tabForward);
EventUtil.addHandler(textbox3, "keyup", tabForward);
})();
14.2.4 HTML5 約束驗證 API
- 必填字段
第一種情況是在表單字段中指定了required
屬性 - 其他輸入類型
- 數(shù)值范圍
- 輸入模式
HTML5 為文本字段新增了pattern
屬性涝婉。這個屬性的值是一個正則表達式,用于匹配文本框中的值蔗怠。
與其他輸入類型相似墩弯,不能阻止用戶輸入無效的文本。 - 檢測有效性
使用checkValidity()
方法可以檢測表單中的某個字段是否有效寞射。
if (document.forms[0].elements[0].checkValidity()) {
//字段有效渔工,繼續(xù)
} else {
//字段無效
}
要檢測整個表單是否有效,可以在表單自身調(diào)用checkValidity()
方法桥温。
if (document.forms[0].checkValidity()) {
//表單有效引矩,繼續(xù)
} else {
//表單無效
}
validity
屬性則會告訴你為什么字段有效或無效。這個對象包含一系列屬性侵浸,每個屬性返回一個布爾值旺韭。
- 禁用驗證
通過設置novalidate
屬性,可以告訴表單不進行驗證掏觉。
如果一個表單中有多個提交按鈕区端,為了指定點擊某個提交按鈕不必驗證表單,可以在相應的按鈕上添加formnovalidate
屬性澳腹。
14.3 選擇框腳本
選擇框是通過<select>
和<option>
元素創(chuàng)建的织盼。為了方便與這個控件交互,除了所有表單字段公有的屬性和方法外酱塔,HTMLSelectElement
類型還提供了下列屬性和方法沥邻。
-
add(newOption, relOption)
:向控件中插入新<option>
元素,其位置在相關項之前羊娃。 -
multiple
:布爾值唐全,表示是否允許多項選擇,等價于 HTML 中的multiple
特性迁沫。 -
options
:控件中所有<option>
元素的HTMLCollection
芦瘾。 -
remove(index)
: 移除給定位置的選項捌蚊。 -
selectedIndex
:基于 0 的選中項的索引,如果沒有選中項近弟,則值為 -1缅糟。對于支持多選的控件,只保存選中項中第一項的索引祷愉。 -
size
:選擇框中可見的行數(shù)窗宦;等價于 HTML 的size
特性。 - 選擇框的
type
屬性不是"select-one"
二鳄,就是"select-multiple"
赴涵,這取決于 HTML 代碼中有沒有multiple
特性。選擇框的value
屬性由當前選中項決定订讼。
在 DOM 中髓窜,每個<option>
元素都有一個HTMLOptionElement
對象表示。為便于訪問數(shù)據(jù)欺殿,HTMLOptionElement
對象添加了下列屬性:
-
index
:當前選項在options
集合中的索引寄纵。 -
label
:當前選項的標簽;等價于 HTML 中的label
特性脖苏。 -
selected
:布爾值程拭,表示當前選項是否被選中。將這個屬性設置為true
可以選中當前選項棍潘。 -
text
:選項的文本恃鞋。 -
value
:選項的值(等價于 HTML 中的value
特性)。
14.3.1 選擇選項
對于只允許選擇一項的選擇框亦歉,訪問選中項的最簡單方式恤浪,就是使用選擇框的selectedIndex
屬性。
var selectedOption = selectbox.options[selectbox.selectedIndex];
多選選擇框鳍徽,設置selectedIndex
屬性會導致取消以前的所有選項并選擇指定的那一項资锰,而讀取selectedIndex
則會返回選中項中第一項的索引值。
另一種選擇選項的方式阶祭,就是取得對某一項的引用绷杜,然后將其selected
屬性設置為true
。
selectbox.options[0].selected = true;
多選框可以通過設置selected
屬性選中任意多個項濒募。在單選框中鞭盟,修改某個選項的selected
屬性則會取消對其他選項的選擇。需要注意的是瑰剃,將selected
屬性設置為false
對單選框沒有影響齿诉。
要取得所有選中的項,可以循環(huán)遍歷選項集合,然后測試每個選項的selected屬性粤剧。如下歇竟。
function getSelectedOption(selectbox) {
var result = new Array();
var option = null;
for (var i=0,len=selectbox.options.length; i < len; i++) {
option = selectbox.options[i];
if (option.selected) {
result.push(option);
}
}
return result;
}
14.3.2 添加選項
第一種方式使用 DOM 方法。第二種方式是使用Option
構造函數(shù)抵恋。
第三種添加新選項的方式是使用選擇框的add()
方法焕议。兼容 DOM 的瀏覽器要求必須指定第二個參數(shù)。
var newOption = new Option("Option text", "Option value");
selectbox.add(newOption, undefined);
如果你想將新選項添加到其他位置(不是最后一個)弧关,就應該使用標準的 DOM 技術和insertBefore()
方法盅安。
14.3.3 移除選項
首先,可以使用 DOM 的removeChild()
方法世囊,為其傳入要移除的選項别瞭。如下所示:
selectbox.removeChild(selectbox.options[0]);
其次,可以使用選擇框的remove()
方法株憾。這個方法可以接受一個參數(shù)蝙寨,即要移除選項的索引,如下所示:
selectbox.remove(0);
最后一種方式号胚,就是將相應選項設置為null
籽慢。這種方式也是 DOM 出現(xiàn)之前瀏覽器的遺留機制浸遗。如:
selectbox.options[0] = null;
要清除選擇框中所有的項猫胁,需要迭代所有選項并逐個移除它們。如下所示:
function clearSelectbox(selectbox) {
for (var i=0, len=selectbox.options.length; i < len; i++) {
selectbox.remove(i?0);
}
}
這個函數(shù)每次只移除選擇框中的第一個選項跛锌。由于移除第一個選項后弃秆,所有后續(xù)選項都會自動向上移動一個位置,因此重復移除第一個選項就可以移除所有選項了髓帽。
14.3.4 移動和重排選項
移動使用 DOM 的appendChild()
方法菠赚。
重排最合適的 DOM 的方法就是insertBefore()
。
14.4 表單序列化
隨著 Ajax 的出現(xiàn)郑藏,表單序列化已經(jīng)成為一種常見需求衡查。可以利用表單字段的type
屬性必盖,連同name
和value
屬性一起實現(xiàn)對表單的序列化拌牲。在編寫代碼之前,必須先搞清楚在表單提交期間歌粥,瀏覽器是怎樣將數(shù)據(jù)發(fā)送給服務器的塌忽。
- 對表單字段的名稱和值進行 URL 編碼,使用和號(&)分隔失驶。
- 不發(fā)送禁用的表單字段土居。
- 只發(fā)送勾選的復選框和單選按鈕。
- 不發(fā)送 type 為“
reset
”和“button
”的按鈕。 - 多選選擇框中的每個選中的值單獨一個條目擦耀。
- 在單擊提交按鈕提交表單的情況下棉圈,也會發(fā)送提交按鈕;否則眷蜓,不發(fā)送提交按鈕迄损。也包括
type
為“image
”的<input>
元素。 -
<select>
元素的值账磺,就是選中的<option>
元素的value
特性的值芹敌。如果<option>
元素沒有value
特性,則是<option>
元素的文本值垮抗。
以下就是實現(xiàn)表單序列化的代碼氏捞。
function serialize(form) {
var parts = [],
field = null,
i,
len,
j,
optLen,
option,
optValue;
for (i=0, len=form.elements.length; i < len; i++) {
field = form.elements[i];
switch(field.type) {
case "select-one":
case "select-multiple":
if (field.name.length) {
for (j=0,optLen = field.options.length; j < optLen; j++) {
option = field.options[j];
if (option.selected) {
optValue = "";
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: //字段集
case "file": //文件輸入
case "submit": //提交按鈕
case "reset": //重置按鈕
case "button": //自定義按鈕
break;
case "radio": //單選按鈕
case "checkbox": //復選框
if(!field.checked) {
break;
}
/* 執(zhí)行默認操作 */
default: //不包含沒有名字的表單字段
if (field.name.length) {
parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value));
}
}
}
return parts.join("&");
}
14.5 富文本編輯
富文本編輯,又稱為 WYSIWYG(What You See Is What You Get冒版,所見即所得)液茎。這一技術的本質(zhì),就是在頁面中嵌入一個包含空 HTML 頁面的 iframe
辞嗡。通過設置designMode
屬性捆等,這個空白的HTML 頁面可以被編輯,而編輯對象則是該頁面<body>
元素的 HTML 代碼续室。designMode
屬性由兩個可能的值:“off
”(默認值)和“on
”栋烤。在設置為“on
”時,整個文檔都會變得可以編輯(顯示插入符號)挺狰,然后就可以像使用字處理軟件一樣明郭,通過鍵盤將文本內(nèi)容加粗、變成斜體丰泊,等等薯定。
可以給iframe
指定一個非常簡單的 HTML 頁面作為其內(nèi)容來源。例如:
<!DOCTYPE html>
<html>
<head>
<title>Blank Page for Rich Text Editing</title>
</head>
<body>
</body>
</html>
只有在頁面完全加載之后才能設置designMode
屬性瞳购。因此话侄,在包含頁面中,需要使用onload
事件處理程序來在恰當?shù)臅r刻設置designMode
学赛,如下所示:
<iframe name="richedit" style="height:100px;width:100px;" src="blank.htm"></iframe>
<script type="text/javascript">
EventUtil.addHandler(window, "load", function() {
frames["richedit"].document.designMode = "on";
});
</script>
等到以上代碼執(zhí)行之后年堆,你就會在頁面中看到一個類似文本框的可編輯區(qū)字段。這個區(qū)字段具有與其他網(wǎng)頁相同的默認樣式罢屈;不過嘀韧,通過為空白頁面應用 CSS 樣式,可以修改可編輯區(qū)字段的外觀缠捌。
14.5.1 使用 contenteditable 屬性
另一種編輯富文本內(nèi)容的方式是使用名為contenteditable
的特殊屬性锄贷,這個屬性也是由 IE 最早實現(xiàn)的译蒂。可以把contenteditable
屬性應用給頁面中的任何元素谊却,然后用戶立即就可以編輯該元素柔昼。不需要iframe
、空白頁和 JavaScript炎辨。
<div class="editable" id="richedit" contenteditable></div>
通過在這個元素上設置contenteditable
屬性捕透,也能打開或關閉編輯模式。
var div = document.getElementById("richedit");
richedit.contentEditable = "true";
contenteditable
屬性有三個可能的值:“true
”表示打開碴萧、“false
”乙嘀、表示關閉、“inherit
”表示從父元素那里繼承(因為可以在contenteditable
元素中創(chuàng)建或刪除元素)破喻。兼容性很強虎谢。
14.5.2 操作富文本
與富文本編輯器交互的主要方式,就是使用document.execCommand()
曹质。這個方法可以對文檔執(zhí)行預定義的命令婴噩,而且可以應用大多數(shù)格式∮鸬拢可以為document.execCommand()
方法傳遞 3 個參數(shù):要執(zhí)行的命令名稱几莽、表示瀏覽器是否應該為當前命令提供用戶界面的一個布爾值和執(zhí)行命令必須的一個值(如果不需要值,則傳遞null
)宅静。為了確闭买迹跨瀏覽器的兼容性,第二個參數(shù)應該始終設置為false
坏为。
不同瀏覽器支持的預定義命令也不一樣究驴。
其中,與剪貼板有關的命令在不同瀏覽器中的差異極大匀伏。Opera 根本沒有實現(xiàn)任何剪貼板命令,而 Firefox 在默認情況下會禁用它們蝴韭。Safari 和 Chrome 實現(xiàn)了cut
和copy
够颠,但沒有實現(xiàn)paste
。不過榄鉴,即使不能通過document.execCommand()
來執(zhí)行這些命令履磨,但卻可以通過相應的快捷鍵來實現(xiàn)同樣的操作。
可以在任何時候使用這些命令來修改富文本區(qū)域的外觀庆尘,如下例剃诅。
//轉換粗體文本
frames["richedit"].document.execCommand("bold", false, null);
//轉換斜體文本
frames["richedit"].document.execCommand("italic", false, null);
//創(chuàng)建指向 www.wrox.com 的鏈接
frames["richedit"].document.execCommand("createlink", false, "http://www.wrox.com");
//格式化為 1 級標題
frames["richedit"].document.execCommand("formatblock", false, "<h1>");
同樣的方法也適用于頁面中contenteditable
屬性為true
的區(qū)塊,只要把對框架的引用替換成當前窗口的document
對象即可驶忌。
需要注意的是矛辕,雖然所有瀏覽器都支持這些命令笑跛,但這些命令所產(chǎn)生的 HTML 不一定相同。
可以使用queryCommandEnabled()
來檢測是否可以針對當前選擇的文本聊品,或者當前插入字符所在位置執(zhí)行某個命令飞蹂。
另外,queryCommandState()
方法用于確定是否已將指定命令應用到了選擇的文本翻屈。
最后陈哑,queryCommandValue()
,用于取得執(zhí)行命令時傳入的值(即前面例子中傳給document.execCommand()
的第三個參數(shù))伸眶。
14.5.3 富文本選區(qū)
在富文本編輯器中惊窖,使用框架(iframe
)的getSelection()
方法,可以確定實際選擇的文本厘贼。這個方法是window
對象和document
對象的屬性爬坑,調(diào)用它會返回一個表示當前選擇文本的Selection
對象。每個Selection
對象都有下列屬性涂臣。
-
anchorNode
:選區(qū)起點所在的節(jié)點盾计。 -
anchorOffset
:在到達選區(qū)起點位置之前跳過的anchorNode
中的字符數(shù)量。 -
focusNode
:選區(qū)終點所在的節(jié)點赁遗。 -
focusOffset
:focusNode
中包含在選區(qū)之內(nèi)的字符數(shù)量署辉。 -
isCollapsed
:布爾值,表示選區(qū)的起點和終點是否重合岩四。 -
rangeCount
:選區(qū)中包含的 DOM 范圍的數(shù)量哭尝。
Selection
對象的這些屬性并沒有包含多少有用的信息。好在剖煌,該對象的方法提供了更多信息材鹦。
IE 8 及更早的版本不支持 DOM 范圍,但我們可以通過它支持的selection
對象操作選擇的文本耕姊。
var range = frame["richedit"].document.selection.createRange();
var selectedText = range.text;
14.5.4 表單與富文本
從技術上說桶唐,富文本編輯器并不屬于表單。換句話說茉兰,富文本編輯器中 HTML 不會被自動提交給服務器尤泽,而需要我們手工來提取并提交 HTML。為此规脸,通撑髟迹可以添加一個隱藏的表單字段,讓它的值等于從iframe
中提取出的 HTML莫鸭。
14.6 小結
使用 JavaScript 可以增強已有的表單字段闹丐,從而創(chuàng)造出新的功能,或者提升表單的易用性被因。
- 可以使用一些標準或非標準的方法選擇文本框的全部或部分文本卿拴。
- 大多數(shù)瀏覽器都采用了 Firefox 操作選擇文本的方式衫仑,但 IE 仍然堅持自己的實現(xiàn)。
- 在文本框的內(nèi)容變化時巍棱,可以通過偵聽鍵盤事件以及檢測插入的字符惑畴,來允許或禁止用戶輸入某些字符。
在文本框內(nèi)容必須限制為某些特定字符的情況下航徙,就可以利用剪貼板事件來屏蔽通過粘貼向文本框中插入內(nèi)容的操作如贷。
選擇框也是經(jīng)常要通過 JavaScript 來控制的一個表單字段。
富文本編輯功能是通過一個包含空 HTML 文檔的iframe
元素來實現(xiàn)的到踏。