<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 表格仅在浏览器中加载

参见