表單操作
表單為頁面的主要組成部分,其中包含許多的表單控件。用戶通過控件提供數(shù)據(jù)并提交給服務(wù)器,服務(wù)器則做出相應(yīng)的處理。而編寫一個正常工作的表單需要三個部分:
- 構(gòu)建表單
- 服務(wù)器處理(提供接受數(shù)據(jù)接口)
- 配置表單
構(gòu)建表單
<form>
<p><label>姓名:<input></label></p>
<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"> 熏肉 </input></label>
<label><input type="checkbox"> 奶酪 </input></label>
<label><input type="checkbox"> 洋蔥 </input></label>
<label><input type="checkbox"> 蘑菇 </input></label>
</fieldset>
<p><label>配送時間:<input type="time" min="11:00" max="2100" step="900"></label></p>
<p><button>提交訂單</button></p>
</form>
服務(wù)器處理
提供接口地址(例如,https://pizza.example.com/order
,數(shù)據(jù)格式(application/x-www-form-urlencoded
),還是接受的參數(shù)信息(custname、custtel、custemail、size、topping、delivery)。
數(shù)據(jù)命名需在表單控件中注明。
配置表單
<form action="https://pizza.example.com/order" method="post" enctype="application/x-www-form-urlencoded">
<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"> 熏肉 </input></label>
<label><input type="checkbox"> 奶酪 </input></label>
<label><input type="checkbox"> 洋蔥 </input></label>
<label><input type="checkbox"> 蘑菇 </input></label>
</fieldset>
<p><label>配送時間:<input type="time" min="11:00" max="2100" step="900"></label></p>
<p><button>提交訂單</button></p>
</form>
用戶所有提交的信息需在提交服務(wù)器前對其進(jìn)行驗證從而提高用戶體驗。
NOTE:表單驗證 使用 require
來強(qiáng)制用戶填寫相應(yīng)的信息。
內(nèi)容
元素
form 元素
form 元素為構(gòu)建表單中最重要的元素。
<form novalidate name="pizza" target="abc" method="post" autocomplete="off" accept-charset="utf-8" action="http://pizza.example.com/order" enctype="application/x-www-form-urlencoded">
其對應(yīng)的信息則可以視為
字段 | 值 |
---|---|
noValidate | true |
target | abc |
method | post |
acceptCharset | utf-8 |
action | http://pizza.example.com/order |
enctype | application/x-www-form-urlencoded |
name | pizza |
autocomplete | off |
NOTE:前六項為表單提交相關(guān)的信息。
屬性
-
name
屬性:可以用于獲取表單節(jié)點元素。
var pizzaForm = document.forms.pizza;
-
autocomplete
屬性:有兩個值on
與off
,在設(shè)置為on
時,可以自動對輸入框進(jìn)行補(bǔ)全(之前提交過的輸入值,下圖左)。
NOTE:在已經(jīng)設(shè)置 autocomplete="off"
時依然出現(xiàn)提示框,大多數(shù)情況為瀏覽器設(shè)置的自動補(bǔ)全(可以強(qiáng)制關(guān)閉,需要時請搜索對應(yīng)的解決方案)。
-
elements
屬性:為一個動態(tài)節(jié)點集合(更具 DOM 的變化進(jìn)行變化),其用于歸結(jié)該表單的子孫表單控件(除圖標(biāo)按鈕外<input type="image>"
):- button
- fieldset
- input
- keygen
- object
- output
- select
- textarea
此外還有歸屬于該表單的空間(依舊圖片按鍵除外)代碼如下所示。
<form id="a">
</form>
<label><input name="null" form="a"></label>
-
length
屬性:等價于elements.length
來用于描述表單內(nèi)節(jié)點集合的個數(shù)。
選取表單空間元素
<form name="test">
<input name="a">
<input name="b">
</form>
選取 name="a"
的控件可以使用下面的方法:
testForm.elements[0];
testForm.elements['a'];
// 操作 Form 表單的屬性
testForm[0];
testForm['a'];
-
form[name]
通過名稱作為索引時有如下特點:- 返回
id
或者name
為指定名稱的表單空間(圖標(biāo)按鍵除外) - 如果結(jié)果為空,則返回
id
為指定名稱的img
元素(入下面代碼所示) - 如果有多個同名元素,則返回的元素為動態(tài)節(jié)點集合
- 一旦用指定名稱取過改元素,之后則不論該元素的
id
或者name
如何變化,只有節(jié)點存在則均可使用原名稱來繼續(xù)獲取改節(jié)點。
- 返回
無指定名稱索引范例
<form name="test">
<img id="a" src="sample.png">
</form>
testForm['a']; // 取得的便是 id 為 a 的圖片元素
更新名稱,依然可以獲取節(jié)點范例
<form name="test">
<input name="a">
</form>
// 第一步
testForm['a'];
// 或者
testForm.elements['a'];
// 第二步
testForm['a'].name = 'b';
form 接口
form
元素也提供了一些接口便于對其進(jìn)行操作 reset()
submit()
checkValidity()
。
可以重置(reset)的元素有下面的幾種:
- input
- keygen
- output
- select
- textarea
當(dāng)觸發(fā)表單 reset
事件時可使用阻止該事件的默認(rèn)行為來取消重置。而且元素重置時將不會再次觸發(fā)元素上的 change
與 input
事件。
label 元素
<label for="textId" form="formId">
字段 | 值 |
---|---|
htmlFor | textId |
control | HTMLElement#textId |
form | HTMLFormElement#formId |
-
htmlFor
屬性:用于關(guān)聯(lián)表單控件的激活行為(可使點擊label
與點擊表單控件的行為一致),可關(guān)聯(lián)的元素有下列(hidden
除外):- button
- input
- keygen
- meter
- output
- progress
- select
- textarea
自定義文件提交控件樣式
-
control
屬性:如果指定了for
屬性則指定該for
屬性對于id
的可關(guān)聯(lián)元素。如果沒有指定for
屬性則為第一個可關(guān)聯(lián)的子孫元素。
可關(guān)聯(lián)的元素 (只讀屬性不可在程序中直接賦值修改)
- button
- fieldset
- input
- keygen
- label
- object
- output
- select
- textarea
- form
屬性:修改關(guān)聯(lián)元素所歸屬的表單則可以修改元素的 form
屬性為帶關(guān)聯(lián)表單Id(元素中對于的for
屬性也應(yīng)該做對應(yīng)的修改)。//這里有一點小問題,更改form屬性之后label并不能自動綁定到新表單對應(yīng)的元素上
label.setAttribute('form', 'newFormId');
input 元素
<input type="text">
-
type
屬性:可用于控制控件的外觀以及數(shù)據(jù)類型(默認(rèn)為text
),在不同的瀏覽器不同數(shù)據(jù)類型有不同的展示效果。
本地圖片預(yù)覽示例
所需技術(shù)點(HTMLInputElement屬性)
- onchange
- accept
- multiple
- files
<input type="file" accept="image/*" multiple>
file.addEventListener(
'change', function(event){
var files = Array.prototype.slice.call(
event.target.files, 0
);
files.forEach(function(item){
files2dataurl(item,function(url){
var image = new Image();
parent.appendChild(image);
image.src = url;
});
});
}
);
function file2dataurl(file, callback) {
if (!window.FileReader) {
throw 'Browser not support File API !';
}
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(event) {
callback(event.target.result);
};
}
NOTE:accept
所支持的格式有 audio/*
video/*
image/*
以及不帶;
的 MINE Type 類型和 .
開頭的文件名后綴的文件。多個文件類型可以使用,
分隔。
select 元素
指定選項列表中選擇需要的選項。
主要的三個子標(biāo)簽 select
、optgroup
(用于選項分組)、option
。
-
select
具有的屬性和方法如下:- name
- value
- multiple
- options(動態(tài)節(jié)點集合)
- selectedOptions(動態(tài)節(jié)點集合)
- selectedIndex
- add(element[, before])(無指定參照物則添加至最末端)
- remove([index])
-
optgroup
所具有的屬性和方法:- disabled (分組選項不可選)
- label(分組說明)
-
option
所具有的屬性和方法:- disabled
- label(描述信息)
- value(提交表單時的數(shù)據(jù)信息)
- text(用戶看到的文字)
- index
- selected
- defaultSelected
選項操作
創(chuàng)建選項
document.createElement('option')
// 或者
new Option([text[, value[, defaultSelected[, selected]]]])
添加選項
var option = new Option('sample');
opt.insertAdjacentElement(option, '參照元素');
// 或者
select.add(option, '參照元素')
刪除選項
opt.parentNode.removeChild(option);
// 或者使用它的索引將其刪除
select.remove(2);
級聯(lián)下列選擇器
所需知識點:
- onchange
- remove
- add
<form name="course">
<select name="chapter">
<option>Select0</option>
</select>
<select name="section">
<option>Select1</option>
</select>
</form>
var chapters = {
{text: 1, value: 1},
{text: 2, value: 2}
};
var sections = {
1: [{
text:1.1, value: 1.1
}, {
text:1.2, value: 1.2
}],
2:[{
text:2.1, value:2.1;
}]
};
function fillSelect(select, list) {
for(var i = select.length; i > 0; i--) {
select.remove(i);
}
list.forEach(function(data){
var option = new Option(data.text, data.value);
select.add(option);
})
}
fileSelect(chapterSelect, chapters);
chapterSelect.addEventListener(
'change', function(event) {
var value = event.target.value,
list = sections[value] || [];
fillSelect(sectionSelect, list);
}
);
textarea 元素
textarea
具有的屬性和方法如下:
- name
- value (用戶輸入信息)
- select() (全選當(dāng)前輸入的內(nèi)容)
- selectionStart (選中的內(nèi)容的起始位置,無選中時返回當(dāng)前光標(biāo)所在位置)
- selectionEnd (選中內(nèi)容結(jié)束位置,無選中時返回光標(biāo)位置)
- selectionDirection (選取方向
forward
backward
) - setSelectionRange(start, end[, direction]) (使用程序選中內(nèi)容)
- setRangeText(replacement[, start, end, [mode]]) (設(shè)置內(nèi)容范圍)
selection
表示選擇區(qū)域,對于 input
元素同樣有效。
selectionDirection
主要是用于在使用 SHIFT 鍵與方向鍵組合選取時的選取方向。設(shè)置為 forward
時選取移動的方向為 selectionEnd
設(shè)置為 backward
時移動方向為 selectionStart
。
@輸入提示示例
所需知識點:
- oninput
- selectionStart
- setRangeText
textarea.addEventListener(
'input', function(event) {
var target = event.target,
cursor = target.selectionStart;
if(target.value.charAt(cursor-1) === '@') {
doShowAtList(functi=on(name){
var end = cursor + name.length;
target.setRangeText(
name, cursor, end, 'end'
);
});
}
}
);
其他元素
- fieldset
- button
- keygen
- output
- progress
- meter
驗證
可以被驗證的元素如下所示:】
- button
- input
- select
- textarea
以下情況不可以做驗證
- input 元素在類型是 hidden, reset, button 時
- button 元素在類型為 reset, button 時
- input 與 textarea 當(dāng)屬性為 readonly 時
- 當(dāng)元素為 datalist 的子孫節(jié)點時
- 當(dāng)元素被禁用時 disabled 的狀態(tài)
屬性
驗證涉及到以下的以下屬性,在每一個可以驗證的元素上均可以調(diào)用對于的屬性或通過接口進(jìn)行操作:
- willValidate (表明此元素在表單提交時是否會被驗證)
- checkValidity() (用于驗證元素,返回 true 當(dāng)驗證通過,或者觸發(fā) invalid 事件)
- validity (存儲驗證結(jié)果)
- validationMessage (顯示驗證異常信息)
- setCustomValidity(message) (自定義驗證錯誤信息)
自定義異常范例
涉及到的知識點:
- oninvalid
- setCustomValidity
<form action="./api" method="post">
<label>Name: <input name="username" required></label>
<button>submit</button>
</form>
input.addEventListener(
'invalid', function(event){
var target = event.target;
if (target.validity.valueMissing) {
target.setCustomValidity('Name is missing');
}
}
)
禁止驗證范例
使用 form
中 novalidate
屬性來禁止表單提交的驗證。
<form action="./api" method="post" novalidate>
<label>Mobile: <input name="mobile" type="number"></label>
<button>submit</button>
</form>
提交
隱式提交
在操作過程中通過控件的操作來提交表單(敲擊回車來提交表單),其需要滿足以下的條件:
- 表單有非禁用的提交按鍵
- 沒有提交按鍵時,不超過一個類型為
text
search
url
email
password
date
time
number
的input
元素
提交過程細(xì)節(jié)
提交過程分為兩個階段,第一個階段是更具表單 enctype 指定的值構(gòu)建要提交的數(shù)據(jù),第二個階段是使用指定的方法(method)發(fā)送數(shù)據(jù)到 action
指定的目標(biāo)。
構(gòu)建提交數(shù)據(jù),從可提交元素中提取數(shù)據(jù)組成指定數(shù)據(jù)結(jié)構(gòu)過程(可提交元素有 button
input
keygen
object
select
textarea
)
編碼方式(enctype)所支持的形式:
- application/x-www-form-urlencoded (默認(rèn),數(shù)據(jù)格式為
&
分隔的鍵值對) - multipart/form-data (IFC 2388 字節(jié)流形式,例如文件上傳所使用的數(shù)據(jù)編碼形式)
- text/plain (回車換行符分隔的鍵值對)
特殊案例一
當(dāng)一個表單元素 name="isindex"
并且 type="text"
而且滿足如下要求時:
- 編碼格式為 application/x-www-form-urlencoded
- 作為表單的第一個元素
則提交時只發(fā)送 value 值,不包含 name。
<form action="./api" method="post">
<input name="isindex">
<input name="a">
<button>submit</button>
</form>
特殊案例二
當(dāng) name="_charset_"
并且類型為 hidden
時,而且滿足如下要求時:
- 沒有設(shè)置
value
值
則提交時 value
自動使用當(dāng)前提交的字符集填充。
submit 接口
form.submit()
可以通過調(diào)用接口submit()
直接提交表單,在提交表單時均會觸發(fā)一個 onsubmit
表單提交事件,在這個事件中 women 可以做下面的事件:
- 提交前的數(shù)據(jù)驗證
- 阻止事件的默認(rèn)行為來取消表單的提交(例如當(dāng)驗證失敗時)
form.addEventListener(
'submit', function(event) {
var notValid = false;
var elements = event.target.elements;
// 自定義驗證
if (notValid) {
// 取消提交
event.preventDefault();
}
}
)
無刷新表單提交范例
常用的方式是通過 AJAX 進(jìn)行實現(xiàn),這里我們使用 iframe 來做中介代理實現(xiàn)。
所需知識點:
- form
- target
- iframe
<iframe name="targetFrame" class="f-hidden" style="display:none" id="result">
<form action="./api" method="post" target="targetFrame">
<input name="isindex">
<input name="a">
<button>submit</button>
</form>
var frame = document.getElementById('result');
frame.addEventListener(
'load', function(event) {
try {
var result = JSON.parse(
frame.contentWindow.document.body.textContent
);
// 還原登陸按鈕狀態(tài)
disabledSubmit(false);
// 識別登陸結(jié)果
if (result.code === 200) {
showMessage('j-suc', 'success');
form.reset();
}
} catch(ex) {
// 忽略操作
}
}
)
表單應(yīng)用
首先需要知道服務(wù)器端登陸接口的相關(guān)信息,如下所示:
描述 | 數(shù)據(jù)信息 |
---|---|
請求地址 | /api/login |
請求參數(shù) |
telephone 手機(jī)號碼; password 密碼 MD5 加密 |
返回結(jié)果 |
code 請求狀態(tài); result 請求數(shù)據(jù)結(jié)果 |
var form = document.forms.loginForm;
var message = document.getElementById('message');
// 通用邏輯封裝
function showMessage(class, message) {
if(!class) {
message.innerHTML = "";
message.classList.remove('j-suc');
message.classList.remove('j-err');
} else {
message.innerHTML = message;
message.classList.add(class);
}
}
function invalidInput (node, message) {
showMessage('j-err', message);
node.classList.add('j-err');
node.focus();
}
function clearInvalid(node){
showMessage();
node.classList.remove('j-err');
}
function disabledSubmit(disabled) {
form.loginBtn.disabled = !!disabled;
var method = !disabled ? 'remove' : 'add';
form.loginBtn.classList[method]('j-disabled');
}
// 驗證手機(jī)號碼(系統(tǒng)自帶方法)
form.telephone.addEventListener(
'invalid', function(event) {
event.preventDefault();
invalidInput(form.telephone, 'invalid mobile number');
}
);
// 驗證密碼
form.addEventListener(
'submit', function(event) {
var input = form.password;
var password = input.value;
errorMessage = '';
if (password.length < 6) {
errorMessage = 'password less than 6 char';
} else if (!/\d./test(password) || !/[a-z]/i.test(password)) {
errorMessage = 'password must contains number and letter'
}
if (!!errorMessage) {
event.preventDefault();
invalidInput(input, errorMessage);
return;
}
// 提交表單代碼
// ...
}
);
// 提交表單
form.addEventListener(
'submit', function(event){
input.value = md5(password);
disabledSubmit(true);
}
);
// 狀態(tài)恢復(fù)
form.addEventListener(
'focus', function(event) {
// 錯誤還原
clearInvalid(event.target);
// 還原登陸按鈕狀態(tài)
disabledSubmit(false);
}
)