27. 選單控制元素 (Selection Menu Controls)

HTML 提供原生控制元素,讓使用者從選項清單中作出選擇。

主要元素包括:

元素用途
<select>提供選擇控制元素
<option>定義可用選項
<optgroup>將相關選項分組
<datalist>為輸入欄位提供建議,而不是限制只能使用列出的選項
<label for="course">選擇課程</label>
<select id="course" name="course">
  <option value="html">HTML 基礎</option>
  <option value="css">CSS 基礎</option>
  <option value="javascript">JavaScript 基礎</option>
</select>

只要原生選擇控制元素能滿足介面需要,就應優先使用它們。 它們提供內建鍵盤操作、表單整合及無障礙支援。

27.1 <select> 元素

<select> 包含使用者可以選擇的選項:

<label for="session">首選上課時段</label>
<select id="session" name="session">
  <option value="morning">上午</option>
  <option value="afternoon">下午</option>
  <option value="evening">晚上</option>
</select>

單選控制元素通常顯示為下拉選單。實際外觀及操作方式取決於瀏覽器和作業系統。

重要屬性包括:

屬性用途
id將控制元素與標籤連接
name識別提交的值
required要求有效選擇
disabled令控制元素不可操作
multiple允許選取多個選項
size要求顯示指定數目的選項列
form透過 ID 將控制元素與表單關聯

<select> 需要結束標籤。

27.2 <option> 元素

每個 <option> 定義一個選項:

<option value="html">HTML 基礎</option>

可見文字與提交值具有不同用途:

  • 可見文字:幫助使用者識別選項。
  • value:提供表單提交及程式碼使用的值。

如果省略 value,選項值會從其文字內容取得。

通常應明確設定值:

<option value="intro-html">HTML 入門介紹</option>

這樣可以改變可見文字,而不會意外改變應用程式使用的識別碼。

為了讓入門標記更清晰,應明確寫出選項的結束標籤。

27.3 初始選擇

使用 selected 設定初始選取的選項:

<label for="language">首選語言</label>
<select id="language" name="language">
  <option value="en" selected>英文</option>
  <option value="zh-Hant">繁體中文</option>
</select>

一般單選下拉選單如果沒有指定選擇,瀏覽器通常會選取第一個可用選項。

不要假設使用者會注意到系統已作出預設選擇。應謹慎設定預設值,尤其當選擇涉及費用、私隱或法律後果時。

selected 是布林屬性:

<!-- 這仍然表示選項初始已被選取。 -->
<option value="en" selected="false">英文</option>

如果不想指定初始選擇,應移除該屬性。

27.4 要求明確選擇

必選的單選下拉選單,可以以空值提示選項開始:

<label for="course">課程(必選)</label>
<select id="course" name="course" required>
  <option value="" selected>請選擇課程</option>
  <option value="html">HTML 基礎</option>
  <option value="css">CSS 基礎</option>
</select>

這種模式中的初始空值選項,不符合必選要求。

標準提示選項行為需要:

  • 單選的 <select>。
  • 顯示大小為一列。
  • 第一個選項具有空值,而且是 <select> 的直接子元素。

提示文字不能取代標籤。

你可以停用提示選項,但應考慮使用者是否需要返回未選擇狀態。對非必選欄位,可選取的「沒有偏好」選項通常很有用。

27.5 使用 <optgroup> 分組

使用 <optgroup> 組織相關選項:

<label for="course">選擇課程</label>
<select id="course" name="course">
  <optgroup label="入門程度">
    <option value="html">HTML 基礎</option>
    <option value="css">CSS 基礎</option>
  </optgroup>

  <optgroup label="中級程度">
    <option value="javascript">JavaScript 應用</option>
    <option value="accessibility">網頁無障礙功能</option>
  </optgroup>
</select>

群組的 label 提供顯示名稱。

重要事項包括:

  • 群組標題不是可選取的選項。
  • <optgroup> 不能互相嵌套。
  • 群組名稱應幫助使用者理解組織方式。
  • 短清單應避免過度分組。

27.6 停用控制元素與選項

個別選項不可用時,可以將它停用:

<option value="evening" disabled>
  晚上 — 名額已滿
</option>

停用整個群組:

<optgroup label="進階課程 — 暫不提供" disabled>
  <option value="advanced-js">進階 JavaScript</option>
  <option value="performance">網頁效能</option>
</optgroup>

停用整個控制元素:

<label for="session">上課時段選擇</label>
<select id="session" name="session" disabled>
  <option value="">暫時未提供上課時段</option>
</select>

停用的 <select>:

  • 通常不能操作,也不能透過一般 Tab 次序到達。
  • 不參與原生限制驗證。
  • 不包含在一般表單提交中。

如果不可用的原因不明顯,應提供可見說明。

<select> 不支援 readonly。另外,單靠 aria-disabled="true" 不會停用原生操作或提交。

27.7 多項選擇

加入 multiple,允許選取多個選項:

<label for="topics">感興趣的主題</label>
<select
  id="topics"
  name="topics"
  multiple
  size="4"
  aria-describedby="topics-help"
>
  <option value="html">HTML</option>
  <option value="css">CSS</option>
  <option value="javascript">JavaScript</option>
  <option value="accessibility">無障礙功能</option>
</select>

<p id="topics-help">
  你可以選擇多個主題。
  選取操作方式取決於你的裝置。
</p>

多選控制元素通常顯示為清單,而不是緊湊的下拉選單。

桌面操作可能需要配合修飾鍵,但不同平台的行為不同。不要假設同一套鍵盤指引適用於所有裝置。

對少量獨立選項,具有標籤的核取方塊通常更容易理解及使用。

27.8 size 屬性

size 要求控制元素顯示指定數目的選項列:

<label for="session">選擇一個上課時段</label>
<select id="session" name="session" size="3">
  <option value="morning">上午</option>
  <option value="afternoon">下午</option>
  <option value="evening">晚上</option>
</select>

這個控制元素仍然只允許選取一個選項,因為沒有 multiple。

顯示多列不代表啟用多項選擇。

實際呈現方式取決於瀏覽器及裝置。

27.9 使用 JavaScript 讀取選擇

單選控制元素可以透過 .value 讀取:

const course = document.getElementById("course");

console.log(course.value);

多選控制元素可以透過 selectedOptions 讀取:

const topics = document.getElementById("topics");

const selectedValues = Array.from(
  topics.selectedOptions,
  (option) => option.value
);

多選控制元素的 .value 只會返回第一個已選選項的值,而不是包含所有選擇的陣列。

回應已確認的選擇變更:

course.addEventListener("change", () => {
  console.log(course.value);
});

避免只因使用者改變選擇,就自動提交表單或前往另一個網頁。獨立操作按鈕通常更容易預期。

27.10 表單提交

表單內具有名稱的 <select>,會提交已選選項的值:

<form action="/course-enquiry" method="post">
  <label for="course">課程</label>
  <select id="course" name="course">
    <option value="html">HTML 基礎</option>
    <option value="css">CSS 基礎</option>
  </select>

  <button type="submit">發送查詢</button>
</form>

選擇 HTML 會產生名稱為 course、值為 html 的欄位。

多選控制元素中,已選且未停用的選項會以相同名稱產生多個資料項目。

可以使用 FormData 查看:

const data = new FormData(form);
const topics = data.getAll("topics");

topics[] 這類名稱是部分伺服器框架使用的慣例,不是 HTML 要求。

應在伺服器端驗證提交值。使用者可以修改用戶端標記,或不經表單直接發送請求。

27.11 <datalist>:建議,而不是限制

<datalist> 為輸入欄位提供建議:

<label for="course-search">感興趣的課程</label>
<input
  id="course-search"
  name="courseInterest"
  type="text"
  list="course-suggestions"
>

<datalist id="course-suggestions">
  <option value="HTML 基礎"></option>
  <option value="CSS 基礎"></option>
  <option value="網頁無障礙功能"></option>
</datalist>

輸入欄位的 list 參照 <datalist> 的 ID。

與 <select> 不同,配合 <datalist> 的文字輸入欄位允許輸入未列出的值。

特點<select>配合 <datalist> 的文字輸入欄位
是否限制只能選擇列出的選項一般操作下是否
是否允許輸入任意文字否是
提交值來源已選選項輸入欄位值
原生呈現方式選擇介面附有建議的輸入欄位

<datalist> 的呈現方式及無障礙支援各有不同。應使用目標瀏覽器及輔助技術測試。

應將它用作有用建議,而不是唯一的允許值限制方式。

27.12 無障礙功能與樣式

良好的選擇控制元素應具有:

  • 持續可見、有意義的標籤。
  • 清晰的選項文字。
  • 操作方式不明顯時所需的指引。
  • 可見焦點提示。
  • 選擇之後可預期的操作。
  • 按需要與控制元素關聯的錯誤訊息。

基本樣式設定很直接:

select {
  display: block;
  width: 100%;
  padding: 0.65rem;
  border: 1px solid #767676;
  border-radius: 0.25rem;
  color: #222;
  background-color: white;
  font: inherit;
}

select:focus-visible {
  outline: 3px solid #005ea8;
  outline-offset: 3px;
}

傳統原生選項選單在不同瀏覽器及作業系統中,具有不同的樣式限制及呈現方式。

不要移除下拉指示,除非提供同樣清晰的替代。完全自訂的選擇元件需要仔細實作焦點、鍵盤導覽、選取狀態及無障礙名稱。

27.13 完整範例:課程選擇

以下獨立示範結合:

  • 必選課程下拉選單。
  • 分組選項。
  • 非必選的上課時段偏好。
  • 多選主題清單。
  • 在本頁顯示已選值的摘要。

網頁不會提交或儲存任何資料。

id: hong-kong-html-selection-menu-controls
name: 選單控制元素 — 課程偏好
type: html
content: |-
  <!doctype html>
  <html lang="zh-Hant-HK">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>選單控制元素 | 課程偏好</title>
    <style>
      *,
      *::before,
      *::after {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        color: #222;
        background: #f5f7fa;
        font-family: system-ui, sans-serif;
        line-height: 1.8;
      }

      main {
        max-width: 42rem;
        margin-inline: auto;
        padding: 2rem 1rem;
      }

      h1 {
        color: #005ea8;
        line-height: 1.4;
      }

      form {
        padding: 1.25rem;
        border: 1px solid #767676;
        border-radius: 0.5rem;
        background: white;
      }

      .field {
        margin-block-end: 1.5rem;
      }

      label {
        display: block;
        margin-block-end: 0.4rem;
        font-weight: 700;
      }

      select {
        display: block;
        width: 100%;
        padding: 0.65rem;
        border: 1px solid #767676;
        border-radius: 0.25rem;
        color: #222;
        background-color: white;
        font: inherit;
      }

      .help {
        margin-block: 0.4rem 0;
        color: #4b5563;
      }

      .note {
        padding: 1rem;
        border-inline-start: 4px solid #005ea8;
        background: #eaf2f8;
      }

      button {
        padding: 0.65rem 1rem;
        border: 2px solid #005ea8;
        border-radius: 0.25rem;
        color: white;
        background: #005ea8;
        font: inherit;
        cursor: pointer;
      }

      button:disabled {
        color: #444;
        background: #e0e0e0;
        border-color: #767676;
        cursor: not-allowed;
      }

      :focus-visible {
        outline: 3px solid #005ea8;
        outline-offset: 3px;
      }

      #selection-summary {
        margin-block-start: 1.5rem;
        padding: 1rem;
        border-inline-start: 4px solid #005ea8;
        background: #eaf2f8;
      }
    </style>
  </head>
  <body>
    <main>
      <h1>課程偏好</h1>

      <p class="note">
        選擇範例偏好,然後在本頁顯示摘要。
        本頁不會提交或儲存你的選擇。
      </p>

      <form id="preferences-form">
        <div class="field">
          <label for="course">課程(必選)</label>
          <select id="course" name="course" required>
            <option value="" selected>請選擇課程</option>

            <optgroup label="入門程度">
              <option value="html">HTML 基礎</option>
              <option value="css">CSS 基礎</option>
            </optgroup>

            <optgroup label="中級程度">
              <option value="javascript">
                JavaScript 應用
              </option>
              <option value="accessibility">
                網頁無障礙功能
              </option>
            </optgroup>
          </select>
        </div>

        <div class="field">
          <label for="session">首選上課時段(選填)</label>
          <select
            id="session"
            name="session"
            aria-describedby="session-help"
          >
            <option value="">沒有偏好</option>
            <option value="morning">上午</option>
            <option value="afternoon">下午</option>
            <option value="evening" disabled>
              晚上 — 暫不提供
            </option>
          </select>
          <p id="session-help" class="help">
            目前不提供晚上時段。
          </p>
        </div>

        <div class="field">
          <label for="topics">感興趣的主題(選填)</label>
          <select
            id="topics"
            name="topics"
            multiple
            size="4"
            aria-describedby="topics-help"
          >
            <option value="structure">網頁結構</option>
            <option value="forms">無障礙表單</option>
            <option value="media">音訊及影片</option>
            <option value="responsive">響應式版面</option>
          </select>
          <p id="topics-help" class="help">
            你可以選擇多個主題。
            選取操作方式取決於你的裝置。
            不必選擇任何主題。
          </p>
        </div>

        <button id="summary-button" type="submit" disabled>
          顯示選擇摘要
        </button>

        <p id="selection-summary" role="status" aria-atomic="true">
          在本頁顯示摘要需要 JavaScript。
        </p>
      </form>
    </main>

    <script>
      const form = document.getElementById("preferences-form");
      const course = document.getElementById("course");
      const session = document.getElementById("session");
      const topics = document.getElementById("topics");
      const button = document.getElementById("summary-button");
      const summary = document.getElementById("selection-summary");

      form.addEventListener("submit", (event) => {
        event.preventDefault();

        const courseText =
          course.selectedOptions[0].textContent.trim();
        const sessionText =
          session.selectedOptions[0].textContent.trim();

        const topicTexts = Array.from(
          topics.selectedOptions,
          (option) => option.textContent.trim()
        );

        const topicSummary = topicTexts.length
          ? topicTexts.join("、")
          : "未選擇";

        summary.textContent =
          `課程:${courseText}。` +
          `時段:${sessionText}。` +
          `主題:${topicSummary}。` +
          "沒有提交或儲存任何資料。";
      });

      button.disabled = false;
      summary.textContent =
        "請選擇偏好,然後顯示摘要。";
    </script>
  </body>
  </html>

在這個範例中:

  • 每個 <select> 都具有持續可見的標籤。
  • 必選課程以空值提示選項開始。
  • 選項群組組織不同課程。
  • 非必選時段包含「沒有偏好」。
  • 不可用時段具有可見說明。
  • 主題清單允許多項選擇。
  • submit 處理程式執行之前,原生驗證會檢查必選課程。
  • JavaScript 透過 selectedOptions 讀取所有已選主題。
  • 使用者啟動按鈕後,摘要才會更新。

對這類短主題清單,核取方塊也是合理選擇,而且通常更容易理解操作方式。

27.14 常見錯誤

錯誤較好的做法
只使用提示選項作為標籤提供持續可見的標籤
需要提交值時省略 name為選單提供適當欄位名稱
以為 size 會啟用多項選擇明確加入 multiple
只從多選控制元素讀取 .value使用 selectedOptions 或 FormData.getAll()
寫成 selected="false"移除布林屬性
以為停用選單會提交資料考慮它不包含在表單資料中的情況
為 <select> 使用 readonly選擇適當替代方式
將 datalist 建議當成強制選項驗證值或使用 <select>
選擇後自動前往另一頁提供清晰操作按鈕
依靠選項顏色傳達意思使用有意義的文字
建立沒有完整鍵盤支援的自訂下拉選單優先使用原生控制元素
相信提交的選項值在伺服器端驗證

本章總結: 使用 <select> 和 <option> 提供固定清單中的選擇,使用 <optgroup> 組織選項,需要選取多項時加入 multiple。提供清晰標籤及可預期操作。<datalist> 只用來提供建議,而提交的選擇仍須在伺服器端驗證。

Scroll to Top