<input type="month">

可用性有限

此特性不是基線特性,因為它在一些最廣泛使用的瀏覽器中不起作用。

型別為 month<input> 元素會建立一個輸入欄位,允許使用者輸入月份和年份。此欄位允許輕鬆輸入月份和年份。其值是一個字串,格式為 YYYY-MM,其中 YYYY 是四位數字的年份,MM 是月份。

試一試

<label for="start">Start month:</label>

<input type="month" id="start" name="start" min="2018-03" value="2018-05" />
label {
  display: block;
  font:
    1rem "Fira Sans",
    sans-serif;
}

input,
label {
  margin: 0.4rem 0;
}

該控制元件的使用者介面通常因瀏覽器而異;目前支援情況參差不齊,只有桌面版 Chrome/Opera 和 Edge 以及大多數現代移動瀏覽器版本具有可用的實現。在不支援 month 輸入的瀏覽器中,該控制元件會優雅地降級為 <input type="text">,儘管可能會自動驗證輸入的文字以確保其格式符合預期。

對於那些使用不支援 month 的瀏覽器的使用者,下面的截圖展示了它在 Chrome 和 Opera 中的樣子。點選右側的向下箭頭會彈出一個日期選擇器,讓你選擇月份和年份。

Month control on Chrome browser

Microsoft Edge month 控制元件看起來是這樣的

Month control on Edge browser

一個表示輸入欄位中輸入的月份和年份值的字串,格式為 YYYY-MM(四位或更多位數字年份,後跟一個連字元(-),再後跟兩位數字月份)。此輸入型別使用的月份字串格式在月份字串中描述。

設定預設值

你可以透過在 value 屬性中包含月份和年份來為輸入控制元件設定預設值,如下所示:

html
<label for="bday-month">What month were you born in?</label>
<input id="bday-month" type="month" name="bday-month" value="2001-06" />

需要注意的一點是,顯示的日期格式與實際的 value 不同;大多數使用者代理會根據使用者作業系統的區域設定,以適合當地的形式顯示月份和年份,而日期 value 始終採用 yyyy-MM 格式。

例如,當上述值提交到伺服器時,它將看起來像 bday-month=1978-06

使用 JavaScript 設定值

你也可以使用 HTMLInputElement.value 屬性在 JavaScript 中獲取和設定日期值,例如:

html
<label for="bday-month">What month were you born in?</label>
<input id="bday-month" type="month" name="bday-month" />
js
const monthControl = document.querySelector('input[type="month"]');
monthControl.value = "2001-06";

附加屬性

除了 <input> 元素通用的屬性外,月份輸入還提供以下屬性。

list

列表屬性的值是位於同一文件中的 <datalist> 元素的 id<datalist> 為此輸入提供了一個預定義值列表,以供使用者建議。列表中與 type 不相容的任何值都不會包含在建議選項中。提供的值是建議,而不是要求:使用者可以從這個預定義列表中選擇,也可以提供不同的值。

max

要接受的最新年份和月份,採用上面部分討論的字串格式。如果輸入到元素中的 value 超過此值,則元素將不透過約束驗證。如果 max 屬性的值不是有效的 yyyy-MM 格式字串,則元素沒有最大值。

該值必須指定一個晚於或等於由 min 屬性指定的年月配對。

min

要接受的最早年份和月份,採用與上面描述的相同的 yyyy-MM 格式。如果元素的 value 小於此值,則元素將不透過約束驗證。如果為 min 指定的值不是有效的年月字串,則輸入沒有最小值。

此值必須是一個年月配對,它早於或等於由 max 屬性指定的年月配對。

readonly

一個布林屬性,如果存在,則表示該欄位不能被使用者編輯。但是,它的 value 仍然可以透過直接設定 HTMLInputElement.value 屬性值的 JavaScript 程式碼來更改。

注意: 由於只讀欄位不能有值,因此 required 對同時指定了 readonly 屬性的輸入沒有影響。

step

step 屬性是一個數字,它指定值必須遵循的粒度,或者特殊值 any,如下所述。只有與步長基數相距整數步長的值才有效。如果指定了 min,則步長基數為 min,否則為 value,如果兩者都未提供,則為 0(Unix 紀元,1970-01)。

對於 month 輸入,step 的值以月為單位。step 的預設值為 1,表示 1 個月。

字串值 any 表示不暗示步進,允許任何值(排除其他約束,如 minmax)。實際上,對於 month 輸入,它與 1 具有相同的效果,因為選擇器 UI 只允許選擇整月。

注意:當用戶輸入的資料不符合步進配置時,使用者代理可能會四捨五入到最近的有效值,當有兩個同樣接近的選項時,優先選擇正方向的數字。

使用月份輸入

與日期相關的輸入(包括 month)乍一看很方便;它們承諾提供一個易於選擇日期的 UI,並且無論使用者區域設定如何,它們都會將傳送到伺服器的資料格式標準化。然而,<input type="month"> 存在問題,因為目前許多主流瀏覽器尚未支援它。

我們將檢視 <input type="month"> 的基本和更復雜的用法,然後在處理瀏覽器支援一節中提供關於緩解瀏覽器支援問題的建議)。

月份的基本用法

<input type="month"> 最基本的用法涉及基本的 <input><label> 元素組合,如下所示:

html
<form>
  <label for="bday-month">What month were you born in?</label>
  <input id="bday-month" type="month" name="bday-month" />
</form>

設定最大和最小日期

你可以使用 minmax 屬性來限制使用者可以選擇的日期範圍。在以下示例中,我們指定了最小月份 1900-01 和最大月份 2013-12

html
<form>
  <label for="bday-month">What month were you born in?</label>
  <input
    id="bday-month"
    type="month"
    name="bday-month"
    min="1900-01"
    max="2013-12" />
</form>

結果是:

  • 只能選擇 1900 年 1 月到 2013 年 12 月之間的月份;超出該範圍的月份無法在控制元件中滾動到。
  • 根據你使用的瀏覽器,你可能會發現指定範圍之外的月份可能無法在月份選擇器中選擇(例如 Edge),或者無效(參見驗證)但仍然可用(例如 Chrome)。

控制輸入大小

<input type="month"> 不支援諸如 size 之類的表單大小屬性。你需要藉助 CSS 來滿足尺寸需求。

驗證

預設情況下,<input type="month"> 不對輸入值應用任何驗證。UI 實現通常不允許你輸入任何不是日期的內容——這很有用——但你仍然可以提交帶有空 month 輸入的表單,或者輸入無效日期(例如,4 月 32 日)。

為了避免這種情況,你可以使用 minmax 來限制可用日期(參見設定最大和最小日期),此外還可以使用 required 屬性使日期填寫成為強制性。因此,支援的瀏覽器會在你嘗試提交超出設定範圍的日期或空日期欄位時顯示錯誤。

我們來看一個例子;這裡我們設定了最小和最大日期,並使該欄位成為必填項:

html
<form>
  <div>
    <label for="month">
      What month would you like to visit (June to Sept.)?
    </label>
    <input
      id="month"
      type="month"
      name="month"
      min="2022-06"
      max="2022-09"
      required />
    <span class="validity"></span>
  </div>
  <div>
    <input type="submit" value="Submit form" />
  </div>
</form>

如果您嘗試在未指定月份和年份(或日期超出設定範圍)的情況下提交表單,瀏覽器將顯示錯誤。現在嘗試使用該示例:

這是給那些沒有使用支援瀏覽器的使用者的截圖:

Month required prompt on Chrome browser

這是上述示例中使用的 CSS。這裡我們利用 :valid:invalid CSS 屬性根據當前值是否有效來設定輸入樣式。我們不得不將圖示放在輸入旁邊的 <span> 上,而不是輸入本身,因為在 Chrome 中,生成的內容放置在表單控制元件內部,無法有效設定樣式或顯示。

css
div {
  margin-bottom: 10px;
  position: relative;
}

input[type="number"] {
  width: 100px;
}

input + span {
  padding-right: 30px;
}

input:invalid + span::after {
  position: absolute;
  content: "✖";
  padding-left: 5px;
}

input:valid + span::after {
  position: absolute;
  content: "✓";
  padding-left: 5px;
}

警告: HTML 表單驗證不能替代確保輸入資料格式正確的指令碼。有人很容易調整 HTML 以繞過驗證,或完全刪除它。也有可能有人完全繞過您的 HTML,直接將資料提交到您的伺服器。如果您的伺服器端程式碼未能驗證收到的資料,當提交格式不正確的資料(或資料過大、型別錯誤等)時,可能會發生災難。

處理瀏覽器支援

如上所述,截至撰寫本文時,使用日期輸入的主要問題是許多主流瀏覽器尚未完全實現它們;桌面版只有 Chrome/Opera 和 Edge 支援,而移動裝置上的大多數現代瀏覽器也支援。例如,Android 版 Chrome 上的 month 選擇器看起來像這樣:

Month picker on Chrome for Android

不支援的瀏覽器會優雅地降級為文字輸入,但這在使用者介面的一致性(呈現的控制元件將不同)和資料處理方面都帶來了問題。

第二個問題是兩者中更嚴重的一個。如前所述,對於 month 輸入,實際值始終規範化為 yyyy-mm 格式。另一方面,在預設配置下,text 輸入不知道日期應該採用什麼格式,這是一個問題,因為人們書寫日期的方式有很多種。例如:

  • mmyyyy (072022)
  • mm/yyyy (07/2022)
  • mm-yyyy (07-2022)
  • yyyy-mm (2022-07)
  • 月份 yyyy (2022 年 7 月)
  • 等等…

解決這個問題的一種方法是在你的 month 輸入上新增一個 pattern 屬性。即使 month 輸入不使用它,如果瀏覽器退回到將其視為 text 輸入,則會使用該模式。例如,嘗試在不支援 month 輸入的瀏覽器中檢視以下演示:

html
<form>
  <div>
    <label for="month">
      What month would you like to visit (June to Sept.)?
    </label>
    <input
      id="month"
      type="month"
      name="month"
      min="2022-06"
      max="2022-09"
      required
      pattern="[0-9]{4}-[0-9]{2}" />
    <span class="validity"></span>
  </div>
  <div>
    <input type="submit" value="Submit form" />
  </div>
</form>

如果您嘗試提交它,您會看到,如果您的輸入不符合模式 nnnn-nn(其中 n 是 0 到 9 的數字),瀏覽器現在會顯示錯誤訊息(並將輸入標記為無效)。當然,這並不能阻止人們輸入無效日期(例如 0000-42),或者遵循模式但格式不正確的日期。

還有一個問題是使用者不一定知道預期的是眾多日期格式中的哪一種。我們還有工作要做。

在所有主流瀏覽器都支援日期輸入一段時間之前,處理表單中日期的最佳跨瀏覽器方式是讓使用者在單獨的控制元件中輸入月份和年份(<select> 元素很受歡迎;請參見下面的實現),或者使用 JavaScript 庫,例如 jQuery 日期選擇器外掛。

示例

在此示例中,我們建立了兩組 UI 元素,每個元素都旨在讓使用者選擇月份和年份。第一個是原生的 month 輸入,另一個是一對 <select> 元素,允許獨立選擇月份和年份,以相容尚不支援 <input type="month"> 的瀏覽器。

HTML

請求月份和年份的表單如下所示:

html
<form>
  <div class="nativeDatePicker">
    <label for="month-visit">What month would you like to visit us?</label>
    <input type="month" id="month-visit" name="month-visit" />
    <span class="validity"></span>
  </div>
  <p class="fallbackLabel">What month would you like to visit us?</p>
  <div class="fallbackDatePicker">
    <div>
      <span>
        <label for="month">Month:</label>
        <select id="month" name="month">
          <option selected>January</option>
          <option>February</option>
          <option>March</option>
          <option>April</option>
          <option>May</option>
          <option>June</option>
          <option>July</option>
          <option>August</option>
          <option>September</option>
          <option>October</option>
          <option>November</option>
          <option>December</option>
        </select>
      </span>
      <span>
        <label for="year">Year:</label>
        <select id="year" name="year"></select>
      </span>
    </div>
  </div>
</form>

ID 為 nativeDatePicker<div> 使用 month 輸入型別請求月份和年份,而 ID 為 fallbackDatePicker<div> 則使用一對 <select> 元素。第一個請求月份,第二個請求年份。

用於選擇月份的 <select> 硬編碼了月份名稱,因為它們不會改變(不考慮本地化)。可用年份列表根據當前年份動態生成(有關這些函式如何工作的詳細解釋,請參見下面的程式碼註釋)。

JavaScript

處理選擇使用哪種方法以及設定要包含在非原生年份 <select> 中的年份列表的 JavaScript 程式碼如下所示。

示例中可能最有趣的部分是特性檢測程式碼。為了檢測瀏覽器是否支援 <input type="month">,我們建立一個新的 <input> 元素,嘗試將其 type 設定為 month,然後立即檢查其型別設定為什麼。不支援 month 型別的瀏覽器將返回 text,因為在不支援時 month 會回退到 text。如果不支援 <input type="month">,我們隱藏原生選擇器並顯示回退選擇器 UI。

js
// Get UI elements
const nativePicker = document.querySelector(".nativeDatePicker");
const fallbackPicker = document.querySelector(".fallbackDatePicker");
const fallbackLabel = document.querySelector(".fallbackLabel");

const yearSelect = document.querySelector("#year");
const monthSelect = document.querySelector("#month");

// Hide fallback initially
fallbackPicker.style.display = "none";
fallbackLabel.style.display = "none";

// Test whether a new date input falls back to a text input or not
const test = document.createElement("input");

try {
  test.type = "month";
} catch (e) {
  console.log(e.description);
}

// If it does, run the code inside the if () {} block
if (test.type === "text") {
  // Hide the native picker and show the fallback
  nativePicker.style.display = "none";
  fallbackPicker.style.display = "block";
  fallbackLabel.style.display = "block";

  // Populate the years dynamically
  // (the months are always the same, therefore hardcoded)
  populateYears();
}

function populateYears() {
  // Get the current year as a number
  const date = new Date();
  const year = date.getFullYear();

  // Make this year, and the 100 years before it available in the year <select>
  for (let i = 0; i <= 100; i++) {
    const option = document.createElement("option");
    option.textContent = year - i;
    yearSelect.appendChild(option);
  }
}

注意:請記住,有些年份有 53 周(請參閱每年週數)!在開發生產應用程式時,您需要考慮這一點。

技術摘要

一個表示月份和年份的字串,或為空。
事件 changeinput
支援的常見屬性 autocomplete, list, readonly, step
IDL 屬性 list, value, valueAsDate, valueAsNumber
DOM 介面 HTMLInputElement
方法 select(), stepDown(), stepUp()
隱式 ARIA 角色 沒有對應的角色

規範

規範
HTML
# 月-狀態-(type=month)

瀏覽器相容性

另見