Forms

大多數網站和應用程式都包含網路表單。跟笑話網站 (如 DoWebsitesNeedToLookToLooklyTheSameInEveryBrowser.com) 可能沒有表單,就連 MachineLearningWorkshop.com (MLW) 源自愚人節的一天笑話,但它其實是假的。MLW 的主要「行動號召」是機器人註冊表單,可讓機器人註冊工作坊。這份表單包含在 <form> 元素中。

HTML <form> 元素會標示文件地標,其中包含用於提交資訊的互動式控制項。在 <form> 中巢狀,您會看到該表單的所有互動式 (非互動式) 表單控制項。

HTML 功能強大,本節將著重於 HTML 的強大功能,說明 HTML 在未加入 JavaScript 的情況下可執行的操作。使用用戶端的表單資料以某種方式更新 UI 通常會涉及 CSS 或 JavaScript,這不在本文討論範圍。我們有完整的「學習表單」課程。我們不會在這裡重複該部分的內容,但會介紹幾個表單控制項,以及相關的 HTML 屬性。

您可以透過表單讓使用者與網站或應用程式互動、驗證輸入的資訊,並將資料提交至伺服器。HTML 屬性可讓您要求使用者選取表單控制項或輸入值。HTML 屬性可定義特定條件,值必須符合這些條件才能有效。當使用者嘗試提交表單時,所有表單控制項值都會經過用戶端限制驗證,且在資料符合必要條件前,系統會阻止提交作業,而且不需要使用 JavaScript。您也可以關閉這項功能:在 <form> 上設定 novalidate 屬性,或者更頻繁地在按鈕上設定 formnovalidate (儲存表單資料供日後使用),以避免驗證。

提交表單

使用者啟動表單內嵌的提交按鈕時,系統就會提交表單。將 <input> 用於按鈕時,「value」是按鈕的標籤,會顯示在按鈕中。使用 <button> 時,標籤是開始與結束 <button> 標記之間的文字。提交按鈕可透過下列兩種方式編寫:

<input type="submit" value="Submit Form">
<button type="submit">Submit Form</button>

如果是簡單的表單,您需要一個 <form> 元素,其中含有一些表單輸入內容,另一個則是提交按鈕。不過,提交表單不只這麼簡單。

<form> 元素的屬性會設定表單提交的 HTTP 方法,以及處理表單提交作業的網址。是的,您可以提交表單、處理表單,並在不使用任何 JavaScript 的情況下載入新頁面。<form> 元素就是這麼強大。

<form> 元素的 actionmethod 屬性值分別定義處理表單資料的網址,以及用於提交資料的 HTTP 方法。根據預設,表單資料會傳送至目前的網頁。否則,請將 action 屬性設為資料應傳送至的網址。

傳送的資料是由表單各種表單控制項的名稱/值配對組成。根據預設,這會包含所有巢狀結構形式的表單控制項,且該表單中含有 name。不過,您可以使用 form 屬性,在 <form> 外部納入表單控制項,並省略在 <form> 內嵌的表單控制項。支援表單控制項和 <fieldset>form 屬性的值為與控制項相關聯的表單的 id,不一定是嵌套在其中的表單。也就是說,表單控制項不必實際巢狀結構在 <form> 中。

method 屬性會定義要求的 HTTP 通訊協定:通常為 GETPOST。使用 GET 時,表單資料會以 name=value 組合的參數字串傳送,並附加至 action 的網址。

使用 POST 時,資料會附加至 HTTP 要求的主體。傳送密碼和信用卡資訊等安全資料時,請務必使用 POST

還有 DIALOG 方法。如果 <form method="dialog"> 位於 <dialog> 中,提交表單會關閉對話方塊;雖然資料不會清除或提交,但會發生提交事件。同樣地,不使用 JavaScript。請參閱對話方塊章節的相關說明。請注意,由於這不會提交表單,您可能需要在提交按鈕上同時加入 formmethod="dialog"formnovalidate

表單按鈕可以包含比本節開頭說明的屬性更廣泛的屬性。如果按鈕包含 formactionformenctypeformmethodformnovalidateformtarget 屬性,則在啟用表單提交作業的按鈕上設定的值,優先於 <form> 上設定的 actionenctypemethodtarget。限制驗證會在表單提交前執行,但只有在已啟用的提交按鈕上沒有 formnovalidate,且 <form> 上沒有 novalidate 時才會執行。

如要擷取用於提交表單的按鈕,請為按鈕指定 name。在提交表單時,如果按鈕沒有名稱或值,就不會隨表單資料一併傳送。

提交表單後

使用者提交填妥的線上表單時,系統會提交相關表單控制項的名稱和值。名稱是 name 屬性的值。這些值來自 value 屬性的內容,或是使用者輸入或挑選的值。<textarea> 的值是內部文字。<select> 的值是所選 <option>value,如果 <option> 不包含 value 屬性,則值為所選選項的內文。

<form method="GET">
  <label for="student">Pick a student:</label>
  <select name="student" id="student">
    <option value="hoover">Hoover Sukhdeep</option>
    <option>Blendan Smooth</option>
    <option value="toasty">Toasty McToastface</option>
  </select>
  <input type="submit" value="Submit Form">
</form>

選取「Hoover Sukhdeep」(或不採取任何動作,因為瀏覽器會顯示並預設選取第一個選項值),然後按一下提交按鈕,系統就會重新載入這個頁面,並將網址設為:

https://web.dev/learn/html/forms?student=hoover

由於第二個選項沒有 value 屬性,因此內文會以值的形式提交。選取「Blendan Smooth」並點選提交按鈕後,系統會重新載入這個頁面,並將網址設為:

https://web.dev/learn/html/forms?student=Blendan+Smooth

提交表單時,傳送的資訊包括所有具有 name 的命名表單控制項的名稱和值,但不包括未選取的核取方塊、未選取的圓形按鈕,以及除了提交表單的按鈕以外的任何按鈕的名稱和值。對於所有其他表單控制項,如果表單控制項有名稱,但未輸入值或預設值,表單控制項的 name 會以空白值提交。

輸入類型有 22 種,因此我們無法一一介紹。請注意,如果您希望使用者輸入資訊,則不一定要加入值,而且通常也不建議這麼做。如果使用者無法編輯 <input> 元素的值,您應一律加入值,包括類型為 hiddenradiocheckboxsubmitbuttonreset 的輸入元素。

建議您為表單控制項使用專屬的 name,這樣可簡化伺服器端資料處理作業,但核取方塊和單選鈕除外。

圓形按鈕

如果您發現在一組圓形按鈕中選取圓形按鈕時,一次只能選取一個按鈕,這是因為 name 屬性所致。您可以為群組中的每個圓形按鈕提供相同的 name,藉此產生「只能選取一個」的效果。

name 依群組不得重複:如果您不小心對兩個不同的群組使用相同的 name,只要在第二個群組中選取圓形按鈕,即可取消選取在第一個群組中具有相同 name 的選取項目。

name 和所選圓形按鈕的 value 會隨表單一併提交。請確保每個圓形按鈕都有相關的 value (通常不重複)。系統不會傳送未選取的圓形按鈕值。

您可以在頁面上建立任意數量的單選群組,每個群組都會獨立運作,只要每個群組都有專屬的 name 即可。

如果您想載入的頁面中,有一個同名群組中的圓形按鈕已選取,請加入 checked 屬性。這個圓形按鈕會與 :default CSS 擬造類別相符,即使使用者選取其他圓形按鈕也一樣。目前選取的圓形按鈕與 :checked 疑似類別相符。

如果使用者必須從一組圓形按鈕中選取圓形控制項,請至少在其中一個控制項中加入 required 屬性。在群組中的圓形按鈕上加入 required 會要求使用者選取表單提交所需的選項,但不一定要是選取的屬性圓形按鈕才有效。此外,請在 <legend> 中清楚指出表單控制項為必填項目。我們稍後會說明如何為圓形按鈕群組和個別按鈕加上標籤。

核取方塊

這麼做可以確保群組中的所有核取方塊具有相同的 name。只有選取的核取方塊會將 namevalue 提交至表單。如果您選取了多個同名的核取方塊,系統會以不同的值 (希望如此) 提交同一個名稱。如果您有多個表單控制項具有相同的名稱,即使這些控制項並非全部都是核取方塊,系統也會將這些控制項全部提交,並以 & 號分隔。

如果您未在核取方塊中加入 value,系統會將所選核取方塊的值預設為 on,這可能會造成不必要的麻煩。如果您有三個名為 chk 的核取方塊,且全部都勾選,表單提交內容就無法解讀:

https://web.dev/learn/html/forms?chk=on&chk=on&chk=on

如要將核取方塊設為必填,請新增 required 屬性。請務必在必須勾選核取方塊或使用任何表單控制項時,通知使用者。將 required 新增至核取方塊,只會將該核取方塊設為必填,不會影響其他同名核取方塊。

標籤和領域

為了讓使用者瞭解如何填寫表單,表單必須可供存取。每個表單控制項都必須加上標籤。 您也需要為表單控制項群組加上標籤。雖然個別輸入、選取和文字區域會標示為 <label>,但群組的單一表單控制項會標示為 <fieldset><legend> 內容。

您可能已註意到,在上述範例中,提交按鈕以外的每個表單控制項都有 <label>。標籤可為表單控制項提供無障礙名稱。按鈕會從內容或值取得可存取的名稱。所有其他表單控制項都需要相關聯的 <label>。如果沒有相關聯的標籤,瀏覽器仍會轉譯表單控制項,但使用者不會知道應提供哪些資訊。

如要明確將表單控制項與 <label> 建立關聯,請在 <label> 上加入 for 屬性:這個值是與之相關聯的表單控制項的 id

<label for="full_name">Your name</label>
<input type="text" id="full_name" name="name">

將標籤與表單控制項建立關聯有幾項優點。標籤會為螢幕閱讀器提供無障礙控制項的名稱,方便螢幕閱讀器使用者存取表單控制項。標籤也是「命中區域」,可增加使用者操作範圍,讓手指靈活度較低的使用者更容易使用網站。如果使用滑鼠,請嘗試點選「你的名稱」標籤的任一處。這樣做可提供輸入焦點。

如要提供隱含的標籤,請在開頭和結尾 <label> 標記之間加入表單控制項。無論從螢幕閱讀器還是指標裝置的角度來看,這兩者都同樣可存取,但不會提供像明確標籤那樣的樣式鉤子。

<label>Your name
  <input type="text" name="name">
</label>

由於標籤是「命中區域」,請勿在明確標籤中加入互動元素,或在隱含標籤中加入標籤表單控制項以外的任何其他互動元件。舉例來說,如果您在標籤中加入連結,雖然瀏覽器會轉譯 HTML,但如果使用者按一下標籤輸入表單控制項,卻被重新導向至新頁面,就會感到困惑。

一般來說,<label> 會位於表單控制項之前,但圓形按鈕和核取方塊除外。這不是必要步驟。 這是常見的使用者體驗模式。「瞭解表單」系列文章提供表單設計相關資訊

對於圓形按鈕和核取方塊群組,標籤會為相關聯的表單控制項提供無障礙名稱;但控制項群組和其標籤也需要標籤。如要為群組加上標籤,請將所有元素歸入 <fieldset>,並使用 <legend> 提供群組標籤。

<fieldset>
  <legend>Who is your favorite student?</legend>
  <ul>
    <li>
      <label>
        <input type="radio" value="blendan" name="machine"> Blendan Smooth
      </label>
    </li>
    <li>
      <label>
        <input type="radio" value="hoover" name="machine"> Hoover Sukhdeep
      </label>
    </li>
    <li>
      <label>
        <input type="radio" value="toasty" name="machine"> Toasty McToastface
      </label>
    </li>
  </ul>
</fieldset>

在這個範例中,隱含 <label> 會為每個標籤提供一個圓形按鈕,而 <legend> 則為圓形按鈕群組的標籤。將 <fieldset> 巢狀在另一個 <fieldset> 內是標準做法。舉例來說,如果表單是一份包含許多問題的問卷,且這些問題分為多個相關問題群組,則「最愛的學生」<fieldset> 可能會巢狀在標示為「最愛的學生」的另一個 <fieldset> 中:

<fieldset>
  <legend>Your favorites:</legend>
  <ul start="6">
    <li>
      <fieldset>
        <legend>Who is your favorite student?</legend>
        <ul>
          <li>
            <!-- the rest of the code here -->

這些元素的預設外觀導致使用率偏低,但您可以使用 CSS 為 <legend><fieldset> 設定樣式。除了所有全域屬性外,<fieldset> 也支援 namedisabledform 屬性。當您停用欄位集時,系統會停用所有巢狀表單控制項。nameform 屬性在 <fieldset> 上用途不大。name 可用於透過 JavaScript 存取欄位集,但欄位集本身不會包含在提交的資料中 (包含巢狀結構中的已命名表單控制項)。

輸入類型和動態鍵盤

如前所述,有 22 種不同類型的輸入內容。在某些情況下,如果使用者的裝置配有動態鍵盤,而該裝置只會在需要時顯示 (例如手機),使用的輸入類型會決定顯示的鍵盤類型。系統會根據所需的輸入類型,為顯示的預設鍵盤進行最佳化。舉例來說,輸入 tel 會顯示專為輸入電話號碼而最佳化的按鍵盤;email 包含 @.;而 url 的動態鍵盤則包含冒號和斜線符號。很抱歉,iPhone 仍未在 url 輸入類型的預設動態鍵盤中加入 :

<input type="tel"> 適用於 iPhone 和兩個不同 Android 手機的鍵盤:

iPhone 鍵盤顯示輸入類型:電話號碼。 Android 鍵盤顯示輸入類型:電話號碼。 Android 鍵盤顯示輸入類型:電話號碼。

<input type="email"> 適用於 iPhone 和兩部不同 Android 手機的鍵盤:

iPhone 鍵盤顯示輸入類型為電子郵件。 Android 鍵盤顯示輸入類型 = 電子郵件。 Android 鍵盤顯示輸入類型 = 電子郵件。

存取麥克風和相機

<input type="file"> 檔案輸入類型支援透過表單上傳檔案。檔案類型不限,可由 accept 屬性定義和限制。可接受的檔案類型清單可以是逗號分隔的副檔名清單、全域類型,或是全域類型和副檔名的組合。例如,accept="video/*, .gif" 可接受任何影片檔案或動畫 GIF。音訊檔案使用「audio/*」,影片檔案使用「video/*」,圖片檔案使用「image/*」。

如果要使用使用者的相機或麥克風建立新的媒體檔案,可以使用媒體擷取規格中定義的 capture 屬性。您可以將值設為 user,以便使用者面向輸入裝置,或設為 environment,以便使用手機的後置鏡頭或麥克風。一般來說,使用 capture 時沒有值可以運作,因為使用者會選擇要使用的輸入裝置。

<label for="avatar">A recent photo of yourself:</label>
<input type="file" capture="user" accept="image/*" name="avatar" id="avatar">

內建驗證

同樣地,如果不加入任何 JavaScript,HTML 就能防止表單提交無效值。

部分 CSS 選取器會根據 HTML 屬性 (包括 :required:optional,如果布林值 required 已設定或未設定)、checked 是否已硬式編碼,以及 disabled 屬性是否存在,比對表單控制項。:read-write 擬造類別會比對元素與 contenteditable 集合,以及預設可編輯的表單控制項,例如 numberpasswordtext 輸入類型 (但不包括核取方塊、圓形按鈕或 hidden 類型等)。如果通常可寫入的元素具有 readonly 屬性集,則會改為比對 :read-only

當使用者在表單控制項中輸入資訊時,CSS UI 選取器 (包括 :valid:invalid:in-range:out-of-range) 會根據狀態切換開啟和關閉。當使用者離開表單控制項時,未完全支援的 :user-invalid:user-valid 擬似類別會相符。

您可以使用 CSS 提供提示,指出表單控制項在使用者與表單互動時是否為必填項目和有效項目。您甚至可以使用 CSS,在表單有效之前,防止使用者點選提交按鈕:

form:invalid [type="submit"] {
  opacity: 50%;
  pointer-events: none;
}

這個 CSS 程式碼片段是反模式。雖然您的使用者介面可能會讓使用者覺得直覺且清楚,但許多使用者會嘗試提交表單,以便顯示錯誤訊息。以這種方式讓提交按鈕顯示為已停用,會導致許多使用者仰賴的限制條件驗證功能無法運作。

套用的 CSS 會根據 UI 目前的狀態持續更新。舉例來說,如果您納入含有限制條件的輸入類型,例如 emailnumberurl 和日期類型,如果值非空值 (非空白),且目前的值不是有效的電子郵件、號碼、網址、日期或時間,則 :invalid CSS 擬類別 會相符。這項持續更新的機制與內建 HTML 限制驗證機制不同,後者只會在使用者嘗試提交表單時才會執行。

內建限制驗證功能僅與使用 HTML 屬性設定的限制相關。雖然您可以根據 :required:valid/:invalid 擬群體類別為元素設定樣式,但瀏覽器會在表單提交時,根據 requiredpatternminmax 甚至 type 屬性提供錯誤訊息。

錯誤訊息:指出多重選擇欄位為必填欄位。

當我們嘗試提交表單,但未選取必要的學生喜好時,限制驗證會因 validityState.valueMissing 錯誤而阻止表單提交。

如果任一 validityState 屬性傳回 true,提交作業就會遭到封鎖,而且瀏覽器會在第一個錯誤的表單控制項中顯示錯誤訊息,因此聚焦。當使用者啟動表單提交作業,且有無效值時,第一個無效的表單控制項會顯示錯誤訊息並接收焦點。如果必要控制項未設定值、如果數值超出範圍,或值不符合 type 屬性所需的類型,表單將不會驗證、無法提交,且會顯示錯誤訊息。

如果 number、日期或時間值低於 min 設定的下限或高於 max 上限,控制項就會設為 :out-of-range (和 :invalid),並在使用者嘗試提交表單時收到 valididityState.rangeUnderflowvalidityState.rangeOverflow 錯誤通知。如果值與 step 值不一致,無論是明確設定或預設為 1,控制項都會變成 :out-of-range (和 :invalid),並出現 validityState.stepMismatch 錯誤。錯誤會以泡泡形式顯示,且預設會提供實用資訊,協助您修正錯誤。

值的長度也有類似的屬性:minlengthmaxlength 屬性會在提交時透過 validityState.tooLongvalidityState.tooShort 通知使用者發生錯誤。maxlength 也會防止使用者輸入過多字元。

使用 maxlength 屬性可能會對使用者體驗造成負面影響。一般來說,允許使用者輸入超過允許字元長度的內容,並提供計數器 (可選擇以 <output> 元素的形式提供),通常能提供更好的使用者體驗。這個計數器不會隨表單提交,使用者可以編輯文字,直到輸出內容不超過允許的最大長度為止。maxlength 可以納入 HTML;如同我們先前討論的所有,它在沒有 JavaScript 的情況下也能運作。接著,在載入時,您可以運用 maxlength 屬性的值,在 JavaScript 中建立這個字元計數器。

部分輸入類型似乎有預設限制,但實際上並沒有。舉例來說,tel 輸入類型會在裝置上提供具備動態鍵盤的數字電話鍵盤,但不會限制有效值。針對這類和其他輸入類型,有 pattern 屬性。您可以指定要比對的規則運算式,以便判斷值是否有效。如果值為空白字串,且該值不是必要值,則不會導致 validityState.patternMismatch 錯誤。如果為必填欄位且留空,系統會向使用者顯示 validityState.valueMissing 的預設錯誤訊息,而非 patternMismatch

就電子郵件而言,validityState.typeMismatch 可能會過於寬鬆,無法滿足您的需求。建議您加入 pattern 屬性,這樣系統就無法將不含 TLD 的內部網路電子郵件地址視為有效地址。您可以使用 pattern 屬性提供值必須相符的規則運算式。要求使用者輸入模式比對時,請務必讓使用者清楚瞭解預期結果。

無須編寫一行 JavaScript 即可完成上述操作,但如果是 HTML API,您可以在限制驗證期間,使用 JavaScript 加入自訂訊息。您也可以使用 JavaScript 更新剩餘字元數量、顯示密碼強度進度條,或透過其他方式動態改善完成度

範例

這個範例的 <dialog> 中有一份表單,內含巢狀 <form>,其中包含三個表單控制項和兩個提交按鈕,並包含清楚的標籤和操作說明。

第一個提交按鈕會關閉對話方塊。使用 formmethod="dialog" 覆寫表單的預設方法,關閉 <dialog>,而不提交資料或清除資料。您也必須加入 formnovalidate,否則瀏覽器會嘗試驗證所有必要欄位是否都有值。使用者可能會想在不輸入任何資料的情況下關閉對話方塊和表單,驗證機制可防止這種情況發生。請加入 aria-label="close",因為「X」是已知的視覺提示,但並非描述性標籤。

表單控制項都含有隱含標籤,因此您不需要加入 idfor 屬性。兩個輸入元素都具有必要屬性,因此為必要元素。數字輸入內容已明確設定 step,以示範如何納入 step。由於 step 預設為 1,因此可以省略這個屬性。

<select> 有預設值,因此不需要使用 required 屬性。系統不會在每個選項中加入 value 屬性,而是將值預設為內部文字。

結尾處的提交按鈕會將表單方法設為 POST。點選後,系統會檢查每個值的有效性。如果所有值皆有效,系統就會提交表單資料、關閉對話方塊,且網頁可能會重新導向至 thankyou.php (即動作網址)。如果缺少任何值,或數值步驟不相符或超出範圍,系統會顯示相關瀏覽器定義的錯誤訊息,且不會提交表單,對話方塊也不會關閉。您可以使用 validityState.setCustomValidity('message here') 方法自訂預設錯誤訊息。請注意,如果您設定了自訂訊息,則訊息必須明確設為空字串 (當一切有效),否則表單將無法提交。

其他注意事項

讓您全面瞭解如何協助使用者在表單中輸入正確資料。為了提供良好的使用者體驗,請務必在必要時加入操作說明和提示,避免使用者犯錯。雖然本節說明如何單獨使用 HTML 提供用戶端驗證,但驗證必須同時在用戶端和伺服器端進行。在填寫表單時,您可以以不顯眼的方式提供驗證,例如在值正確時加入勾號。不過,請勿在表單控制項完成前提供錯誤訊息。如果使用者確實犯錯,請告知使用者錯誤所在,以及他們做錯了什麼。

設計表單時,請務必記住,名稱、地址格式等全球各有不同的標準。某人可能會使用單一字母做為姓氏 (或根本沒有姓氏)、沒有郵遞區號、使用三行街道地址,或沒有街道地址。這位使用者可能正在查看表單的翻譯版本。

表單控制項、標籤和錯誤訊息應顯示在畫面上,且應透過程式輔助方式確定正確且有意義的,並以程式輔助方式與適當的表單元素或群組建立關聯。autocomplete 屬性可用於加快表單填寫速度,並改善無障礙功能,而且應予以使用。

HTML 提供所有工具,可讓基本表單控制項更容易存取。表單元素或程序的互動性越高,就越需要重視相關的無障礙功能,包括焦點管理、設定及更新 ARIA 名稱、角色和值 (視需要),以及 ARIA 即時公告。不過,如同我們在本篇文章中所學到的,只要使用 HTML,您就能大幅達成無障礙和有效性目標,而無須使用 ARIA 或 JavaScript。

進行隨堂測驗

測驗您對表單的瞭解程度。

如何讓圓形按鈕屬於同一個群組?

將所有項目放入 fieldset 中。
請再試一次。
為這些屬性提供相同的 name 屬性值。
答對了!
為這些屬性提供相同的 id 屬性值。
請再試一次。

哪一個 HTML 元素可用來向使用者說明這個表單欄位的功用?

<h1>
請再試一次。
<title>
請再試一次。
<label>
答對了!