Forms

大多數的網站和應用程式都包含網路表單。笑話網站 (例如 DoWebsitesNeedToAtlyTheSameItEveryBrowser.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。限制驗證會在提交表單前進行,但前提是已啟用的提交按鈕及 <form> 上沒有 formnovalidate,也沒有 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」(或不執行任何動作,因為瀏覽器會顯示,因此預設選取第一個選項值),然後按一下「Submit」按鈕,即可重新載入這個頁面,並將網址設為:

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 相同的任何選取項目。

所選圓形按鈕的 namevalue 會連同表單一併提交。確認每個圓形按鈕都有相關 (且通常不重複) 的 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 輸入類型的預設動態鍵盤中加入 :

iPhone 和兩支不同 Android 手機的 <input type="tel"> 鍵盤:

iPhone 鍵盤顯示輸入 type=tel。 顯示輸入 type=tel 的 Android 鍵盤。 顯示輸入 type=tel 的 Android 鍵盤。

iPhone 和兩支不同 Android 手機的 <input type="email"> 鍵盤:

iPhone 鍵盤顯示輸入 type=email。 顯示輸入 type=email 的 Android 鍵盤。 顯示輸入 type=email 的 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),與表單控制項相符;如果已設定布林值 required,則:default如果 checked 是硬式編碼;而 :enabled:disabled 則取決於元素是否為 :disableddisabled: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 程式碼片段是反模式,雖然您的 UI 可能覺得直覺易懂,但許多使用者會嘗試提交表單來啟用錯誤訊息。讓提交按鈕顯示為停用,並不允許限制驗證,因為這是許多使用者仰賴的功能。

已套用的 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.rangeUnderflow 的通知,validityState.rangeOverflow 錯誤。如果值不在包含 step 值的步驟中,無論是明確設為 1 還是預設為 1,控制項就是 :out-of-range (和 :invalid),且會發生 validityState.stepMismatch 錯誤。根據預設,錯誤訊息會以對話框形式顯示,並提供解決問題的實用資訊。

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

使用 maxlength 屬性可能導致使用者體驗不佳。一般而言,讓使用者能輸入超過系統規定的字元長度上限,並可選擇採用 <output> 元素的形式 (未透過表單提交),讓使用者可以向下編輯文字,直到輸出顯示超過允許的長度上限為止。您可以在 HTML 中加入 maxlength;如同我們先前討論的任何內容,在沒有 JavaScript 的情況下也能運作。然後,載入時可以使用 maxlength 屬性的值,在 JavaScript 中建立這個字元計數器。

部分輸入類型似乎有預設限制條件,但不會有預設限制。舉例來說,tel 輸入類型在具有動態鍵盤的裝置上提供數字電話鍵盤,但不會限制有效值。而其他輸入類型則是 pattern 屬性。您可以指定值符合的有效規則運算式。 如果值為空字串,且此值非必要,則不會造成 validityState.patternMismatch 錯誤。如果必要且空白,使用者會看到 validityState.valueMissing 的預設錯誤訊息,而非 patternMismatch

如果是電子郵件,validityState.typeMismatch 可能不足以滿足您的需求。建議您加入 pattern 屬性,讓系統將沒有 TLD 的內部網路電子郵件地址視為有效。模式屬性可讓您提供值必須相符的規則運算式。要求比對模式時,請確保使用者能清楚瞭解預期的回應。

無需單行 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 即時公告和 ARIA 即時公告方面,必須獲得更多注意力,以無障礙的方式管理。不過,正如我們在這裡所學,如果只使用 HTML,不必改用 ARIA 或 JavaScript,就能大幅提高無障礙和有效性。

隨堂測驗

測驗您對表單的相關知識。

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

將所有欄位放在欄位集內。
請再試一次。
請為所有屬性提供相同的 name 屬性值。
答對了!
請為所有屬性提供相同的 id 屬性值。
請再試一次。

哪一個 HTML 元素的用途是讓使用者知道這個表單欄位的用途?

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