JavaScript 表單,F(xiàn)ormData 對象

2023-03-20 15:43 更新

表單概述 

表單(<form>)用來收集用戶提交的數(shù)據(jù),發(fā)送到服務(wù)器。比如,用戶提交用戶名和密碼,讓服務(wù)器驗證,就要通過表單。表單提供多種控件,讓開發(fā)者使用,具體的控件種類和用法請參考 HTML 語言的教程。本章主要介紹 JavaScript 與表單的交互。

<form action="/handling-page" method="post">
  <div>
    <label for="name">用戶名:</label>
    <input type="text" id="name" name="user_name" />
  </div>
  <div>
    <label for="passwd">密碼:</label>
    <input type="password" id="passwd" name="user_passwd" />
  </div>
  <div>
    <input type="submit" id="submit" name="submit_button" value="提交" />
  </div>
</form>

上面代碼就是一個簡單的表單,包含三個控件:用戶名輸入框、密碼輸入框和提交按鈕。

用戶點擊“提交”按鈕,每一個控件都會生成一個鍵值對,鍵名是控件的name屬性,鍵值是控件的value屬性,鍵名和鍵值之間由等號連接。比如,用戶名輸入框的name屬性是user_name,value屬性是用戶輸入的值,假定是“張三”,提交到服務(wù)器的時候,就會生成一個鍵值對user_name=張三

所有的鍵值對都會提交到服務(wù)器。但是,提交的數(shù)據(jù)格式跟<form>元素的method屬性有關(guān)。該屬性指定了提交數(shù)據(jù)的 HTTP 方法。如果是 GET 方法,所有鍵值對會以 URL 的查詢字符串形式,提交到服務(wù)器,比如/handling-page?user_name=張三&user_passwd=123&submit_button=提交。下面就是 GET 請求的 HTTP 頭信息。

GET /handling-page?user_name=張三&user_passwd=123&submit_button=提交
Host: example.com

如果是 POST 方法,所有鍵值對會連接成一行,作為 HTTP 請求的數(shù)據(jù)體發(fā)送到服務(wù)器,比如user_name=張三&user_passwd=123&submit_button=提交。下面就是 POST 請求的頭信息。

POST /handling-page HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 74

user_name=張三&user_passwd=123&submit_button=提交

注意,實際提交的時候,只要鍵值不是 URL 的合法字符(比如漢字“張三”和“提交”),瀏覽器會自動對其進行編碼。

點擊submit控件,就可以提交表單。

<form>
  <input type="submit" value="提交">
</form>

上面表單就包含一個submit控件,點擊這個控件,瀏覽器就會把表單數(shù)據(jù)向服務(wù)器提交。

注意,表單里面的<button>元素如果沒有用type屬性指定類型,那么默認(rèn)就是submit控件。

<form>
  <button>提交</button>
</form>

上面表單的<button>元素,點擊以后也會提交表單。

除了點擊submit控件提交表單,還可以用表單元素的submit()方法,通過腳本提交表單。

formElement.submit();

表單元素的reset()方法可以重置所有控件的值(重置為默認(rèn)值)。

formElement.reset()

FormData 對象 

概述 

表單數(shù)據(jù)以鍵值對的形式向服務(wù)器發(fā)送,這個過程是瀏覽器自動完成的。但是有時候,我們希望通過腳本完成這個過程,構(gòu)造或編輯表單的鍵值對,然后通過腳本發(fā)送給服務(wù)器。瀏覽器原生提供了 FormData 對象來完成這項工作。

FormData()首先是一個構(gòu)造函數(shù),用來生成表單的實例。

var formdata = new FormData(form);

FormData()構(gòu)造函數(shù)的參數(shù)是一個 DOM 的表單元素,構(gòu)造函數(shù)會自動處理表單的鍵值對。這個參數(shù)是可選的,如果省略該參數(shù),就表示一個空的表單。

下面是一個表單。

<form id="myForm" name="myForm">
  <div>
    <label for="username">用戶名:</label>
    <input type="text" id="username" name="username">
  </div>
  <div>
    <label for="useracc">賬號:</label>
    <input type="text" id="useracc" name="useracc">
  </div>
  <div>
    <label for="userfile">上傳文件:</label>
    <input type="file" id="userfile" name="userfile">
  </div>
<input type="submit" value="Submit!">
</form>

我們用FormData()處理上面這個表單。

var myForm = document.getElementById('myForm');
var formData = new FormData(myForm);

// 獲取某個控件的值
formData.get('username') // ""

// 設(shè)置某個控件的值
formData.set('username', '張三');

formData.get('username') // "張三"

實例方法 

FormData 提供以下實例方法。

  • FormData.get(key):獲取指定鍵名對應(yīng)的鍵值,參數(shù)為鍵名。如果有多個同名的鍵值對,則返回第一個鍵值對的鍵值。
  • FormData.getAll(key):返回一個數(shù)組,表示指定鍵名對應(yīng)的所有鍵值。如果有多個同名的鍵值對,數(shù)組會包含所有的鍵值。
  • FormData.set(key, value):設(shè)置指定鍵名的鍵值,參數(shù)為鍵名。如果鍵名不存在,會添加這個鍵值對,否則會更新指定鍵名的鍵值。如果第二個參數(shù)是文件,還可以使用第三個參數(shù),表示文件名。
  • FormData.delete(key):刪除一個鍵值對,參數(shù)為鍵名。
  • FormData.append(key, value):添加一個鍵值對。如果鍵名重復(fù),則會生成兩個相同鍵名的鍵值對。如果第二個參數(shù)是文件,還可以使用第三個參數(shù),表示文件名。
  • FormData.has(key):返回一個布爾值,表示是否具有該鍵名的鍵值對。
  • FormData.keys():返回一個遍歷器對象,用于for...of循環(huán)遍歷所有的鍵名。
  • FormData.values():返回一個遍歷器對象,用于for...of循環(huán)遍歷所有的鍵值。
  • FormData.entries():返回一個遍歷器對象,用于for...of循環(huán)遍歷所有的鍵值對。如果直接用for...of循環(huán)遍歷 FormData 實例,默認(rèn)就會調(diào)用這個方法。

下面是get()、getAll()、set()、append()方法的例子。

var formData = new FormData();

formData.set('username', '張三');
formData.append('username', '李四');
formData.get('username') // "張三"
formData.getAll('username') // ["張三", "李四"]

formData.append('userpic[]', myFileInput.files[0], 'user1.jpg');
formData.append('userpic[]', myFileInput.files[1], 'user2.jpg');

下面是遍歷器的例子。

var formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

for (var key of formData.keys()) {
  console.log(key);
}
// "key1"
// "key2"

for (var value of formData.values()) {
  console.log(value);
}
// "value1"
// "value2"

for (var pair of formData.entries()) {
  console.log(pair[0] + ': ' + pair[1]);
}
// key1: value1
// key2: value2

// 等同于遍歷 formData.entries()
for (var pair of formData) {
  console.log(pair[0] + ': ' + pair[1]);
}
// key1: value1
// key2: value2

表單的內(nèi)置驗證 

自動校驗 

表單提交的時候,瀏覽器允許開發(fā)者指定一些條件,它會自動驗證各個表單控件的值是否符合條件。

<!-- 必填 -->
<input required>

<!-- 必須符合正則表達式 -->
<input pattern="banana|cherry">

<!-- 字符串長度必須為6個字符 -->
<input minlength="6" maxlength="6">

<!-- 數(shù)值必須在1到10之間 -->
<input type="number" min="1" max="10">

<!-- 必須填入 Email 地址 -->
<input type="email">

<!-- 必須填入 URL -->
<input type="URL">

如果一個控件通過驗證,它就會匹配:valid的 CSS 偽類,瀏覽器會繼續(xù)進行表單提交的流程。如果沒有通過驗證,該控件就會匹配:invalid的 CSS 偽類,瀏覽器會終止表單提交,并顯示一個錯誤信息。

input:invalid {
  border-color: red;
}
input,
input:valid {
  border-color: #ccc;
}

checkValidity() 

除了提交表單的時候,瀏覽器自動校驗表單,還可以手動觸發(fā)表單的校驗。表單元素和表單控件都有checkValidity()方法,用于手動觸發(fā)校驗。

// 觸發(fā)整個表單的校驗
form.checkValidity()

// 觸發(fā)單個表單控件的校驗
formControl.checkValidity()

checkValidity()方法返回一個布爾值,true表示通過校驗,false表示沒有通過校驗。因此,提交表單可以封裝為下面的函數(shù)。

function submitForm(action) {
  var form = document.getElementById('form');
  form.action = action;
  if (form.checkValidity()) {
    form.submit();
  }
}

willValidate 屬性 

控件元素的willValidate屬性是一個布爾值,表示該控件是否會在提交時進行校驗。

// HTML 代碼如下
// <form novalidate>
//   <input id="name" name="name" required />
// </form>

var input = document.querySelector('#name');
input.willValidate // true

validationMessage 屬性 

控件元素的validationMessage屬性返回一個字符串,表示控件不滿足校驗條件時,瀏覽器顯示的提示文本。以下兩種情況,該屬性返回空字符串。

  • 該控件不會在提交時自動校驗
  • 該控件滿足校驗條件
// HTML 代碼如下
// <form><input type="text" required></form>
document.querySelector('form input').validationMessage
// "請?zhí)顚懘俗侄巍?

下面是另一個例子。

var myInput = document.getElementById('myinput');
if (!myInput.checkValidity()) {
  document.getElementById('prompt').innerHTML = myInput.validationMessage;
}

setCustomValidity() 

控件元素的setCustomValidity()方法用來定制校驗失敗時的報錯信息。它接受一個字符串作為參數(shù),該字符串就是定制的報錯信息。如果參數(shù)為空字符串,則上次設(shè)置的報錯信息被清除。

這個方法可以替換瀏覽器內(nèi)置的表單驗證報錯信息,參數(shù)就是要顯示的報錯信息。

<form action="somefile.php">
  <input
    type="text"
    name="username"
    placeholder="Username"
    pattern="[a-z]{1,15}"
    id="username"
  >
  <input type="submit">
</form>

上面的表單輸入框,要求只能輸入小寫字母,且不得超過15個字符。如果輸入不符合要求(比如輸入“ABC”),提交表單的時候,Chrome 瀏覽器會彈出報錯信息“Please match the requested format.”,禁止表單提交。下面使用setCustomValidity()方法替換掉報錯信息。

var input = document.getElementById('username');
input.oninvalid = function (event) {
  event.target.setCustomValidity(
    '用戶名必須是小寫字母,不能為空,最長不超過15個字符'
  );
}

上面代碼中,setCustomValidity()方法是在invalid事件的監(jiān)聽函數(shù)里面調(diào)用。該方法也可以直接調(diào)用,這時如果參數(shù)不為空字符串,瀏覽器就會認(rèn)為該控件沒有通過校驗,就會立刻顯示該方法設(shè)置的報錯信息。

/* HTML 代碼如下
<form>
  <p><input type="file" id="fs"></p>
  <p><input type="submit"></p>
</form>
*/

document.getElementById('fs').onchange = checkFileSize;

function checkFileSize() {
  var fs = document.getElementById('fs');
  var files = fs.files;
  if (files.length > 0) {
     if (files[0].size > 75 * 1024) {
       fs.setCustomValidity('文件不能大于 75KB');
       return;
     }
  }
  fs.setCustomValidity('');
}

上面代碼一旦發(fā)現(xiàn)文件大于 75KB,就會設(shè)置校驗失敗,同時給出自定義的報錯信息。然后,點擊提交按鈕時,就會顯示報錯信息。這種校驗失敗是不會自動消除的,所以如果所有文件都符合條件,要將報錯信息設(shè)為空字符串,手動消除校驗失敗的狀態(tài)。

validity 屬性 

控件元素的屬性validity屬性返回一個ValidityState對象,包含當(dāng)前校驗狀態(tài)的信息。

該對象有以下屬性,全部為只讀屬性。

  • ValidityState.badInput:布爾值,表示瀏覽器是否不能將用戶的輸入轉(zhuǎn)換成正確的類型,比如用戶在數(shù)值框里面輸入字符串。
  • ValidityState.customError:布爾值,表示是否已經(jīng)調(diào)用setCustomValidity()方法,將校驗信息設(shè)置為一個非空字符串。
  • ValidityState.patternMismatch:布爾值,表示用戶輸入的值是否不滿足模式的要求。
  • ValidityState.rangeOverflow:布爾值,表示用戶輸入的值是否大于最大范圍。
  • ValidityState.rangeUnderflow:布爾值,表示用戶輸入的值是否小于最小范圍。
  • ValidityState.stepMismatch:布爾值,表示用戶輸入的值不符合步長的設(shè)置(即不能被步長值整除)。
  • ValidityState.tooLong:布爾值,表示用戶輸入的字?jǐn)?shù)超出了最長字?jǐn)?shù)。
  • ValidityState.tooShort:布爾值,表示用戶輸入的字符少于最短字?jǐn)?shù)。
  • ValidityState.typeMismatch:布爾值,表示用戶填入的值不符合類型要求(主要是類型為 Email 或 URL 的情況)。
  • ValidityState.valid:布爾值,表示用戶是否滿足所有校驗條件。
  • ValidityState.valueMissing:布爾值,表示用戶沒有填入必填的值。

下面是一個例子。

var input = document.getElementById('myinput');
if (input.validity.valid) {
  console.log('通過校驗');
} else {
  console.log('校驗失敗');
}

下面是另外一個例子。

var txt = '';
if (document.getElementById('myInput').validity.rangeOverflow) {
  txt = '數(shù)值超過上限';
}
document.getElementById('prompt').innerHTML = txt;

如果想禁止瀏覽器彈出表單驗證的報錯信息,可以監(jiān)聽invalid事件。

var input = document.getElementById('username');
var form  = document.getElementById('form');

var elem = document.createElement('div');
elem.id  = 'notify';
elem.style.display = 'none';
form.appendChild(elem);

input.addEventListener('invalid', function (event) {
  event.preventDefault();
  if (!event.target.validity.valid) {
    elem.textContent   = '用戶名必須是小寫字母';
    elem.className     = 'error';
    elem.style.display = 'block';
    input.className    = 'invalid animated shake';
  }
});

input.addEventListener('input', function(event){
  if ( 'block' === elem.style.display ) {
    input.className = '';
    elem.style.display = 'none';
  }
});

上面代碼中,一旦發(fā)生invalid事件(表單驗證失敗),event.preventDefault()用來禁止瀏覽器彈出默認(rèn)的驗證失敗提示,然后設(shè)置定制的報錯提示框。

表單的 novalidate 屬性 

表單元素的 HTML 屬性novalidate,可以關(guān)閉瀏覽器的自動校驗。

<form novalidate>
</form>

這個屬性也可以在腳本里設(shè)置。

form.noValidate = true;

如果表單元素沒有設(shè)置novalidate屬性,那么提交按鈕(<button><input>元素)的formnovalidate屬性也有同樣的作用。

<form>
  <input type="submit" value="submit" formnovalidate>
</form>

enctype 屬性 

表單能夠用四種編碼,向服務(wù)器發(fā)送數(shù)據(jù)。編碼格式由表單的enctype屬性決定。

假定表單有兩個字段,分別是foobaz,其中foo字段的值等于bar,baz字段的值是一個分為兩行的字符串。

The first line.
The second line.

下面四種格式,都可以將這個表單發(fā)送到服務(wù)器。

(1)GET 方法

如果表單使用GET方法發(fā)送數(shù)據(jù),enctype屬性無效。

<form
  action="register.php"
  method="get"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

數(shù)據(jù)將以 URL 的查詢字符串發(fā)出。

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.

(2)application/x-www-form-urlencoded

如果表單用POST方法發(fā)送數(shù)據(jù),并省略enctype屬性,那么數(shù)據(jù)以application/x-www-form-urlencoded格式發(fā)送(因為這是默認(rèn)值)。

<form
  action="register.php"
  method="post"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

發(fā)送的 HTTP 請求如下。

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A

上面代碼中,數(shù)據(jù)體里面的%0D%0A代表換行符(\r\n)。

(3)text/plain

如果表單使用POST方法發(fā)送數(shù)據(jù),enctype屬性為text/plain,那么數(shù)據(jù)將以純文本格式發(fā)送。

<form
  action="register.php"
  method="post"
  enctype="text/plain"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

發(fā)送的 HTTP 請求如下。

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.

(4)multipart/form-data

如果表單使用POST方法,enctype屬性為multipart/form-data,那么數(shù)據(jù)將以混合的格式發(fā)送。

<form
  action="register.php"
  method="post"
  enctype="multipart/form-data"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

發(fā)送的 HTTP 請求如下。

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--

這種格式也是文件上傳的格式。

文件上傳 

用戶上傳文件,也是通過表單。具體來說,就是通過文件輸入框選擇本地文件,提交表單的時候,瀏覽器就會把這個文件發(fā)送到服務(wù)器。

<input type="file" id="file" name="myFile">

此外,還需要將表單<form>元素的method屬性設(shè)為POSTenctype屬性設(shè)為multipart/form-data。其中,enctype屬性決定了 HTTP 頭信息的Content-Type字段的值,默認(rèn)情況下這個字段的值是application/x-www-form-urlencoded,但是文件上傳的時候要改成multipart/form-data。

<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">選擇一個文件</label>
    <input type="file" id="file" name="myFile" multiple>
  </div>
  <div>
    <input type="submit" id="submit" name="submit_button" value="上傳" />
  </div>
</form>

上面的 HTML 代碼中,file 控件的multiple屬性,指定可以一次選擇多個文件;如果沒有這個屬性,則一次只能選擇一個文件。

var fileSelect = document.getElementById('file');
var files = fileSelect.files;

然后,新建一個 FormData 實例對象,模擬發(fā)送到服務(wù)器的表單數(shù)據(jù),把選中的文件添加到這個對象上面。

var formData = new FormData();

for (var i = 0; i < files.length; i++) {
  var file = files[i];

  // 只上傳圖片文件
  if (!file.type.match('image.*')) {
    continue;
  }

  formData.append('photos[]', file, file.name);
}

最后,使用 Ajax 向服務(wù)器上傳文件。

var xhr = new XMLHttpRequest();

xhr.open('POST', 'handler.php', true);

xhr.onload = function () {
  if (xhr.status !== 200) {
    console.log('An error occurred!');
  }
};

xhr.send(formData);

除了發(fā)送 FormData 實例,也可以直接 AJAX 發(fā)送文件。

var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();

xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);

參考鏈接 


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號