使用 HTML 表單驗證和約束驗證 API
建立 Web 表單一直是一項複雜的任務。雖然表單本身的標記很容易,但檢查每個欄位是否具有有效且一致的值則更加困難,並且告知使用者問題可能會成為一個令人頭疼的問題。HTML5 引入了新的表單機制:它為 <input> 元素添加了新的語義型別,並引入了約束驗證,以簡化客戶端檢查表單內容的工作。透過設定新屬性,無需 JavaScript 即可檢查基本的常見約束;可以使用約束驗證 API 測試更復雜的約束。
有關這些概念的基本介紹(包括示例),請參閱表單驗證教程。
注意:HTML 約束驗證不能消除伺服器端驗證的必要性。儘管預計無效表單請求會少得多,但仍可以透過多種方式傳送無效請求
- 透過瀏覽器的開發人員工具修改 HTML。
- 透過手動建立 HTTP 請求而不使用表單。
- 透過程式設計方式將內容寫入表單(某些約束驗證僅針對使用者輸入執行,而不是在你使用 JavaScript 設定表單欄位值時執行)。
因此,你應始終在伺服器端驗證表單資料,使其與客戶端所做的一致。
固有和基本約束
在 HTML 中,基本約束以兩種方式宣告
語義輸入型別
type 屬性的固有約束包括
| 輸入型別 | 約束描述 | 關聯的違規 |
|---|---|---|
<input type="URL"> |
該值必須是 URL 即時標準中定義的絕對 URL。 | TypeMismatch 約束違規 |
<input type="email"> |
該值必須是語法上有效的電子郵件地址,通常格式為 username@hostname.tld,但也可以是本地格式,例如 username@hostname。 |
TypeMismatch 約束違規 |
對於這兩種輸入型別,如果設定了 multiple 屬性,則可以設定多個值,作為逗號分隔列表。如果其中任何一個不滿足此處描述的條件,則會觸發 型別不匹配 約束違規。
請注意,大多數輸入型別沒有固有約束,因為有些輸入型別被排除在約束驗證之外,或者具有將不正確的值轉換為正確預設值的淨化演算法。
與驗證相關的屬性
除了上面描述的 type 屬性外,以下屬性用於描述基本約束
| 屬性 | 支援該屬性的輸入型別 | 可能的值 | 約束描述 | 關聯的違規 |
|---|---|---|---|---|
pattern
|
text、search、url、tel、email、password |
一個 JavaScript 正則表示式(global、ignoreCase 和 multiline 標誌停用編譯) |
該值必須與模式匹配。 |
patternMismatch 約束違規 |
min
|
range、number |
一個有效數字 | 該值必須大於或等於該值。 |
rangeUnderflow 約束違規 |
date、month、week |
一個有效日期 | |||
datetime-local、time |
一個有效日期和時間 | |||
max
|
range、number |
一個有效數字 | 該值必須小於或等於該值 |
rangeOverflow 約束違規 |
date、month、week |
一個有效日期 | |||
datetime-local、time |
一個有效日期和時間 | |||
required
|
text、search、url、tel、email、password、date、datetime-local、month、week、time、number、checkbox、radio、file;也適用於 <select> 和 <textarea> 元素 |
無,因為它是一個布林屬性:它的存在表示真,它的不存在表示假 | 必須有一個值(如果設定)。 |
valueMissing 約束違規 |
step
|
日期 |
一個整數天數 | 除非步長設定為 any 字面量,否則該值必須是 min + 步長的整數倍。 |
stepMismatch 約束違規 |
月份 |
一個整數月數 | |||
周 |
一個整數週數 | |||
datetime-local、time |
一個整數秒數 | |||
range、number |
一個整數 | |||
minlength
|
text、search、url、tel、email、password;也適用於 <textarea> 元素 |
一個整數長度 | 如果非空,則字元數(程式碼點)不得小於屬性值。對於 <textarea>,所有換行符都規範化為單個字元(而不是 CRLF 對)。 |
tooShort 約束違規 |
maxlength
|
text、search、url、tel、email、password;也適用於 <textarea> 元素 |
一個整數長度 | 字元數(程式碼點)不得超過屬性值。 |
tooLong 約束違規 |
約束驗證過程
約束驗證透過約束驗證 API 在單個表單元素上或在表單級別(在 <form> 元素本身上)完成。約束驗證以下列方式完成
- 透過呼叫與表單關聯的 DOM 介面(
HTMLInputElement、HTMLSelectElement、HTMLButtonElement、HTMLOutputElement或HTMLTextAreaElement)的checkValidity()或reportValidity()方法,該方法僅評估此元素上的約束,允許指令碼獲取此資訊。checkValidity()方法返回一個布林值,指示元素的值是否透過其約束。(這通常由使用者代理在確定適用哪個 CSS 偽類:valid或:invalid時完成。)相反,reportValidity()方法向用戶報告任何約束失敗。 - 透過呼叫
HTMLFormElement介面上的checkValidity()或reportValidity()方法。 - 透過提交表單本身。
呼叫 checkValidity() 稱為靜態驗證約束,而呼叫 reportValidity() 或提交表單稱為互動式驗證約束。
備註
- 如果
<form>元素上設定了novalidate屬性,則不會發生約束的互動式驗證。 - 呼叫
HTMLFormElement介面上的submit()方法不會觸發約束驗證。換句話說,即使表單資料不滿足約束,此方法也會將表單資料傳送到伺服器。請改為呼叫提交按鈕上的click()方法。 minlength和maxlength約束僅在使用者提供的輸入上進行檢查。如果以程式設計方式設定值,即使顯式呼叫checkValidity()或reportValidity(),也不會檢查它們。
使用約束驗證 API 的複雜約束
使用 JavaScript 和約束 API,可以實現更復雜的約束,例如,組合多個欄位的約束,或涉及複雜計算的約束。
基本思想是在某些表單欄位事件(例如 onchange)上觸發 JavaScript,以計算約束是否被違反,然後使用 field.setCustomValidity() 方法設定驗證結果:空字串表示約束得到滿足,任何其他字串表示存在錯誤,並且此字串是向用戶顯示錯誤訊息。
組合多個欄位的約束:郵政編碼驗證
郵政編碼格式因國家/地區而異。許多國家/地區允許使用可選的國家/地區程式碼字首(例如德國的 D-、法國的 F- 和瑞士的 CH-)。一些國家/地區在郵政編碼中僅使用固定數量的數字,而其他國家/地區(例如英國)則具有更復雜的格式,允許在某些特定位置使用字母。
注意:這不是一個全面的郵政編碼驗證庫,而是對關鍵概念的演示。
作為示例,我們將新增一個檢查表單約束驗證的指令碼
<form>
<label for="postal-code">Postal Code: </label>
<input type="text" id="postal-code" />
<label for="country">Country: </label>
<select id="country">
<option value="ch">Switzerland</option>
<option value="fr">France</option>
<option value="de">Germany</option>
<option value="nl">The Netherlands</option>
</select>
<input type="submit" value="Validate" />
</form>
這會顯示以下表單
首先,我們編寫一個檢查約束本身的函式
const countrySelect = document.getElementById("country");
const postalCodeField = document.getElementById("postal-code");
function checkPostalCode() {
// For each country, defines the pattern that the postal code has to follow
const constraints = {
ch: [
"^(CH-)?\\d{4}$",
"Swiss postal codes must have exactly 4 digits: e.g. CH-1950 or 1950",
],
fr: [
"^(F-)?\\d{5}$",
"French postal codes must have exactly 5 digits: e.g. F-75012 or 75012",
],
de: [
"^(D-)?\\d{5}$",
"German postal codes must have exactly 5 digits: e.g. D-12345 or 12345",
],
nl: [
"^(NL-)?\\d{4}\\s*([A-RT-Z][A-Z]|S[BCE-RT-Z])$",
"Dutch postal codes must have exactly 4 digits, followed by 2 letters except SA, SD and SS",
],
};
// Read the country id
const country = countrySelect.value;
// Build the constraint checker
const constraint = new RegExp(constraints[country][0], "");
console.log(constraint);
// Check it!
if (constraint.test(postalCodeField.value)) {
// The postal code follows the constraint, we use the ConstraintAPI to tell it
postalCodeField.setCustomValidity("");
} else {
// The postal code doesn't follow the constraint, we use the ConstraintAPI to
// give a message about the format required for this country
postalCodeField.setCustomValidity(constraints[country][1]);
}
}
然後我們將其連結到 <select> 的 change 事件和 <input> 的 input 事件
countrySelect.addEventListener("change", checkPostalCode);
postalCodeField.addEventListener("input", checkPostalCode);
限制檔案上傳前的大小
另一個常見的約束是限制要上傳的檔案的大小。在檔案傳輸到伺服器之前在客戶端進行檢查需要結合約束驗證 API,特別是 field.setCustomValidity() 方法,以及另一個 JavaScript API,此處是 File API。
這是 HTML 部分
<label for="fs">Select a file smaller than 75 kB: </label>
<input type="file" id="fs" />
這會顯示
JavaScript 讀取選定的檔案,使用 File.size() 方法獲取其大小,將其與(硬編碼的)限制進行比較,並呼叫約束 API 以告知瀏覽器是否存在違規
const fs = document.getElementById("fs");
function checkFileSize() {
const files = fs.files;
// If there is (at least) one file selected
if (files.length > 0) {
if (files[0].size > 75 * 1000) {
// Check the constraint
fs.setCustomValidity("The selected file must not be larger than 75 kB");
fs.reportValidity();
return;
}
}
// No custom constraint violation
fs.setCustomValidity("");
}
最後,我們將該方法與正確的事件關聯起來
fs.addEventListener("change", checkFileSize);
約束驗證的視覺樣式
除了設定約束外,Web 開發人員還希望控制向用戶顯示的訊息以及它們的樣式。
控制元素的樣式
元素的樣式可以透過 CSS 偽類控制。
:required 和 :optional CSS 偽類
:required 和 :optional 偽類允許編寫選擇器,匹配具有 required 屬性的表單元素,或不具有該屬性的表單元素。
:placeholder-shown CSS 偽類
請參閱 :placeholder-shown。
:valid :invalid CSS 偽類
:valid 和 :invalid 偽類用於表示根據輸入型別設定,內容分別透過驗證和未能透過驗證的 <input> 元素。這些類允許使用者設定有效或無效表單元素的樣式,以便更容易識別格式正確或不正確的元素。
控制約束違規的文字
以下各項有助於控制約束違規的文字
-
以下元素上的
setCustomValidity(message)方法<fieldset>。注意:在大多數瀏覽器中,對 fieldset 元素設定自定義驗證訊息不會阻止表單提交。<input><output><select>- 提交按鈕(透過型別為
submit的<button>元素或型別為 submit 的input元素建立)。其他型別的按鈕不參與約束驗證。 <textarea>
-
ValidityState介面描述了上述元素型別的validity屬性返回的物件。它表示輸入值無效的各種方式。它們共同幫助解釋了為什麼元素的值無法驗證(如果它無效)。