約束驗證

建立 Web 表單一直是一項複雜的任務。雖然標記表單本身很容易,但檢查每個欄位是否具有有效且一致的值更難,告知使用者問題可能會讓人頭疼。 HTML5 為表單引入了新的機制:它為 <input> 元素添加了新的語義型別以及約束驗證,以簡化在客戶端檢查表單內容的工作。基本且常見的約束可以在沒有 JavaScript 的情況下進行檢查,方法是設定新屬性;更復雜的約束可以使用約束驗證 API 進行測試。

有關這些概念的基本介紹,以及示例,請參閱 表單驗證教程

注意:HTML 約束驗證不會消除對伺服器端驗證的需求。即使預期收到無效表單請求的數量要少得多,但仍然可以透過多種方式傳送無效請求

  • 透過瀏覽器開發者工具修改 HTML。
  • 透過手工製作 HTTP 請求,而不用表單。
  • 透過程式設計方式將內容寫入表單(某些約束驗證*僅對*使用者輸入執行,而不會在使用 JavaScript 設定表單欄位的值時執行)。

因此,您應該始終在伺服器端驗證表單資料,與客戶端的操作保持一致。

內在約束和基本約束

在 HTML 中,基本約束以兩種方式宣告

  • 選擇對type 屬性的最語義值<input> 元素,例如,選擇email 型別會自動建立一個約束,檢查該值是否為有效的電子郵件地址。
  • 透過設定與驗證相關的屬性值,允許以簡單的方式描述基本約束,無需使用 JavaScript。

語義輸入型別

用於type 屬性的內在約束是

輸入型別 約束描述 關聯違規
<input type="URL"> 該值必須是一個絕對URL,如URL 實施標準中所定義。 TypeMismatch 約束違規
<input type="email"> 該值必須是語法上有效的電子郵件地址,通常格式為username@hostname.tld,但也可以是本地的,例如username@hostname TypeMismatch 約束違規

對於這兩種輸入型別,如果multiple 屬性已設定,則可以設定多個值,以逗號分隔的列表形式。如果這些值中任何一個不滿足此處描述的條件,則會觸發Type mismatch 約束違規。

請注意,大多數輸入型別沒有內在約束,因為某些型別被禁止進行約束驗證,或者具有一個將不正確值轉換為正確預設值的清理演算法。

除了上面描述的type 屬性外,以下屬性用於描述基本約束

屬性 支援該屬性的輸入型別 可能的值 約束描述 關聯違規
pattern text, search, url, tel, email, password 一個JavaScript 正則表示式(使用globalignoreCasemultiline 標誌*停用*編譯) 該值必須與模式匹配。 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> 元素 none 因為它是一個布林屬性:存在表示true,不存在表示false 必須有一個值(如果已設定)。 valueMissing 約束違規
step date 一個整天的天數 除非 step 設定為any 文字,否則該值必須是min 加上 step 的整數倍。 stepMismatch 約束違規
month 一個整數的月數
week 一個整數的週數
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 介面的checkValidity()reportValidity() 方法(HTMLInputElementHTMLSelectElementHTMLButtonElementHTMLOutputElementHTMLTextAreaElement),這僅評估該元素上的約束,允許指令碼獲取此資訊。checkValidity() 方法返回一個布林值,指示元素的值是否透過其約束。(這通常由使用者代理在確定使用哪種 CSS 偽類:valid:invalid 時完成。)相反,reportValidity() 方法將任何約束失敗報告給使用者。
  • 透過呼叫HTMLFormElement 介面上的checkValidity()reportValidity() 方法。
  • 透過提交表單本身。

呼叫checkValidity() 被稱為*靜態地*驗證約束,而呼叫reportValidity() 或提交表單被稱為*互動式地*驗證約束。

注意

  • 如果novalidate 屬性已設定在<form> 元素上,則不會進行約束的互動式驗證。
  • HTMLFormElement 介面上呼叫submit() 方法不會觸發約束驗證。換句話說,即使該方法不滿足約束,也會將表單資料傳送到伺服器。改為在提交按鈕上呼叫click() 方法。
  • minlengthmaxlength 約束僅對使用者提供的輸入進行檢查。即使顯式呼叫checkValidity()reportValidity(),也不會在以程式設計方式設定值時檢查它們。

使用約束驗證 API 的複雜約束

使用 JavaScript 和約束 API,可以實現更復雜的約束,例如,組合多個欄位的約束,或涉及複雜計算的約束。

基本上,其理念是在某些表單欄位事件(如onchange)上觸發 JavaScript 以計算約束是否被違反,然後使用field.setCustomValidity() 方法設定驗證結果:空字串表示約束滿足,任何其他字串都表示出現錯誤,該字串是顯示給使用者的錯誤訊息。

組合多個欄位的約束:郵政編碼驗證

郵政編碼格式因國家/地區而異。大多數國家/地區不僅允許使用國家/地區程式碼的可選字首(例如德國的D-,法國或瑞士的F-),而且有些國家/地區的郵政編碼只有固定位數;其他國家/地區,如英國,具有更復雜的結構,允許在某些特定位置使用字母。

注意:這不是一個完整的郵政編碼驗證庫,而只是一個對關鍵概念的演示。

例如,我們將新增一個指令碼,用於檢查此簡單表單的約束驗證

html
<form>
  <label for="ZIP">ZIP : </label>
  <input type="text" id="ZIP" />
  <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>

這將顯示以下表單

首先,我們編寫一個函式來檢查約束本身

js
function checkZIP() {
  // For each country, defines the pattern that the ZIP has to follow
  const constraints = {
    ch: [
      "^(CH-)?\\d{4}$",
      "Switzerland ZIPs must have exactly 4 digits: e.g. CH-1950 or 1950",
    ],
    fr: [
      "^(F-)?\\d{5}$",
      "France ZIPs must have exactly 5 digits: e.g. F-75012 or 75012",
    ],
    de: [
      "^(D-)?\\d{5}$",
      "Germany ZIPs 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])$",
      "Netherland ZIPs must have exactly 4 digits, followed by 2 letters except SA, SD and SS",
    ],
  };

  // Read the country id
  const country = document.getElementById("Country").value;

  // Get the NPA field
  const ZIPField = document.getElementById("ZIP");

  // Build the constraint checker
  const constraint = new RegExp(constraints[country][0], "");
  console.log(constraint);

  // Check it!
  if (constraint.test(ZIPField.value)) {
    // The ZIP follows the constraint, we use the ConstraintAPI to tell it
    ZIPField.setCustomValidity("");
  } else {
    // The ZIP doesn't follow the constraint, we use the ConstraintAPI to
    // give a message about the format required for this country
    ZIPField.setCustomValidity(constraints[country][1]);
  }
}

然後,我們將它連結到<select>onchange 事件和<input>oninput 事件

js
window.onload = () => {
  document.getElementById("Country").onchange = checkZIP;
  document.getElementById("ZIP").oninput = checkZIP;
};

限制檔案上傳之前的大小

另一個常見的約束是限制要上傳的檔案的大小。在將檔案傳輸到伺服器之前在客戶端檢查這一點,需要組合約束驗證 API,尤其是field.setCustomValidity() 方法,以及另一個 JavaScript API,此處為檔案 API。

以下是 HTML 部分

html
<label for="FS">Select a file smaller than 75 kB : </label>
<input type="file" id="FS" />

這將顯示

JavaScript 讀取選定的檔案,使用File.size() 方法獲取其大小,將其與(硬編碼的)限制進行比較,並呼叫約束 API 以告知瀏覽器是否存在違規

js
function checkFileSize() {
  const FS = document.getElementById("FS");
  const files = FS.files;

  // If there is (at least) one file selected
  if (files.length > 0) {
    if (files[0].size > 75 * 1024) {
      // Check the constraint
      FS.setCustomValidity("The selected file must not be larger than 75 kB");
      FS.reportValidity();
      return;
    }
  }
  // No custom constraint violation
  FS.setCustomValidity("");
}

最後,我們將該方法與正確的事件掛鉤

js
window.onload = () => {
  document.getElementById("FS").onchange = 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 屬性返回的物件。它表示輸入值無效的各種方式。它們共同幫助解釋了為什麼元素的值無效(如果它無效)。