<input type="month">

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

試一試

控制的 UI 在不同的瀏覽器中通常會有所不同;目前支援參差不齊,只有桌上型電腦上的 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

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;如果兩者都沒有提供,則為適當的預設值)才是有效的。

any的字串值表示沒有隱含的步進,允許任何值(不包括其他約束,例如minmax)。

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

對於month輸入,step的值以月為單位,縮放係數為 1(因為底層數值也是以月為單位)。step的預設值為 1 個月。

使用月份輸入

乍一看,與日期相關的輸入(包括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 在桌面版上支援它,而大多數現代瀏覽器在移動版上支援它。例如,Chrome for Android 上的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)
  • Month 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 date picker外掛。

示例

在此示例中,我們建立了兩組 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 回退的型別。如果<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
支援的常用屬性 autocompletelistreadonlystep
IDL 屬性 listvaluevalueAsDatevalueAsNumber
DOM 介面

HTMLInputElement

方法 select()stepDown()stepUp()
隱式 ARIA 角色 沒有相應的角色

規範

規範
HTML 標準
# month-state-(type=month)

瀏覽器相容性

BCD 表格僅在瀏覽器中載入

參見