<input type="month">
类型为 month
的 <input>
可以让你容易地创建一个方便输入年份或月份的一个 <input>
。输入的值是一个经过“YYYY-MM
”格式化的字符串,其中 YYYY
是四位数的年份,而 MM
是月份的数值表示。
尝试一下
通常来说控件的 UI 界面因浏览器的不同而有变化,到目前为止此控件还不被所有浏览器支持,在桌面浏览器中只有 Chrome/Opera 和 Edge 支持;在移动端被大部分现代浏览器所支持。在不支持的浏览器中,这个控件会被优雅的降级到 <input type="text">
,不过对输入的文字会有自动的验证,保证它按照预期进行格式化。
对于那些使用不支持 month
控制器的浏览器用户,以下的截图呈现了 Chrome 和 Opera 的月份控制器。单击右侧的向下箭头会显示日期选择器,以便选择年份和月份。
Edge 的 month
控制器看起来像这样的:
值 | 代表月份和年份的字符串,或为空字符串 |
事件 | change 和 input |
支持的共有属性 | autocomplete 、list 、readonly 和 step |
IDL 属性 | value |
DOM 接口 | |
方法 | select() 、stepDown() 和 stepUp() |
值
一个以 YYYY-MM 形式(4 个或更多位数的年,接一个连字符 -
,再接一个两位数月份)表示月份和年份的字符串。月份字符串的格式在月份字符串中有描述。
设置默认值
通过 JavaScript 代码设置 value
当然你也可以使用 JavaScript 代码通过 HTMLInputElement.value
属性来获取或设置日期的值。例如:
<label for="bday-month">你在哪个月出生?</label>
<input id="bday-month" type="month" name="bday-month" />
const monthControl = document.querySelector('input[type="month"]');
monthControl.value = "2001-06";
其他属性
除了 <input>
元素的共有属性外,month 输入还提供以下属性:
list
列表属性的值是位于同一文档中的 <datalist>
元素的 id
。<datalist>
提供了一个预定义的值列表,向用户建议这个输入。列表中任何与 type
不兼容的值都不包括在建议选项中。所提供的值是建议,不是要求:用户可以从这个预定义的列表中选择,或者提供不同的值。
max
min
readonly
一个布尔属性,如果存在,则表示该字段不能由用户编辑。但是,仍可以通过 JavaScript 代码直接设置 HTMLInputElement.value
属性来更改。
备注:因为只读字段不能有值,所以 required
对指定了 readonly
属性的输入没有任何影响。
step
step
属性指定了值必须满足的粒度,或者是下文描述的特殊值 any
。值必须满足基础的步进值,才有效。如果指定了 min
属性,则由 min
属性决定,否则,使用 value
属性的值,如果上述两个值都不存在,则提供适当的默认值。
字符串值 any
意味着不使用步进值,任意值都可以接受(除其他制约因素如 min
或 max
之外)。
备注:当用户输入的数据不符合步进配置时,用户代理可能会四舍五入到最近的有效值,当有两个同样接近的选项时,更倾向于正方向的数字。
对于 month
输入,step
的值以月份为单位,缩放因子为 1(基础数值也是以月份为单位的)。step
的默认值为 1,表示 1 个月。
使用 month 输入
与日期相关的输入(包含 month
)乍一看很方便:它们提供了一个简单的用户界面来选择日期,并且它们将发送到服务器的数据格式规范化,无论用户的本地化配置如何。但是,<input type="month">
还是存在兼容性问题,大多数主流浏览器还没有支持它。
我们先看看 <input type="month">
的基础和高级的用法,然后再讨论有关缓解浏览器支持问题的建议(请参阅处理浏览器支持问题)。
基本使用方法
设置最大和最小日期
你可以使用 min
和 max
属性来限制用户选择日期的范围。在下列的例子中我们指定最小月份 1900-01
和最大月份 2013-12
:
<form>
<label for="bday-month">你在哪个月出生?</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">
不会对输入的值应用任何验证,用户界面实现会屏蔽所有非日期值的输入,尽管这很有用,但是仍然无法完全依赖于该值的合法性。你仍然可以不填入任何值而提交,或输入不合法的日期值(如 4 月 32 日)。
为了避免这种情况,你可以使用 min
和 max
来限制可用的日期(参见设置最大和最小日期),并使用 required
属性令日期必填。在支持的浏览器中,当你尝试提交超出范围的日期,或空日期值时,会显示错误信息。
我们来看一个例子,这里我们设定了最小和最大的日期,并令该字段必填:
<form>
<div>
<label for="month"> 你愿意在哪个月拜访(6 月至 9 月)? </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="提交表单" />
</div>
</form>
如果你尝试在未指定月份或年份,或指定超出给定范围的日期情况下提交表单,浏览器会显示错误。请试试以下实时演示:
对于那些没有使用支持的浏览器的人们来说,以下是该错误的屏幕截图:
下面是上述例子中使用的 CSS。这里我们使用了 :valid
和 :invalid
这两个 CSS 属性,根据当前值是否有效来为输入添加样式。我们不得不把图标放在 input 旁边的 <span>
上,而不是放在 input 框本身,因为在 Chrome 中,生成的内容被放在表单控件里面,无法有效地进行样式设计或显示。
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 代码,直接将数据提交给你的服务器。如果你的服务器端代码不能验证它所收到的数据,那么当提交的数据格式不当(或数据过大、类型错误等等)时,灾难就会降临。
处理浏览器支持
如前所述,使用 month 输入的最大问题是大部分主流浏览器还没有实现它们;只有桌面端的 Chrome/Opera、Edge 和大部分的移动端现代浏览器支持它们。例如,安卓版 Chrome 的 month
选择器看起来像这样:
不支持的浏览器会优雅地降级为文本输入,但这在用户界面的一致性(呈现的控件会有所不同)和数据处理方面都会产生问题。
第二个问题更为严重;如前所述,month
输入的值总是被规范为 yyyy-mm
的格式。另一方面,对于文本输入,默认情况下,浏览器不知道时间应该是什么格式,而且人们有多种写法,如:
mmyyyy
(072022)mm/yyyy
(07/2022)mm-yyyy
(07-2022)yyyy-mm
(2022-07)Month yyyy
(July 2022)- 等等
一个办法是在你的 month
输入上添加 pattern
属性。即使 month
输入不使用它,text 输入回退也会使用。例如,试着在一个不支持 month 输入的浏览器中查看下面的演示:
<form>
<div>
<label for="month"> 愿意在哪个月访问(6 月至 9 月)? </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="提交表单" />
</div>
</form>
如果你尝试提交,你会发现,如果你的输入不符合模式 nnnn-nn
(其中 n
是 0 到 9 的数字),不支持的浏览器现在会显示一个错误信息(并突出显示输入无效)。当然,这并不能阻止人们输入无效的日期(如 0000-42
),或者不正确但遵循格式的日期。
还有一个问题,就是用户不知道到底应该输入什么格式的日期。还有一些事情要做。
目前,以跨浏览器方式处理时间的最佳方法(至少在所有主流浏览器实现它们之前)是让用户在单独的控件中输入月份和年份(特别是在 <select>
元素中,参见下面的示例),或使用 JavaScript 库(例如 jQuery 日期选择器插件)。
示例
在此示例中,我们创建了两组用于选择日期的接口元素:使用 <input type="month">
创建的原生选择器,以及为不支持 month 输入类型的旧版浏览器准备的两个分别用于选择月份和年份的 <select>
元素。
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
,然后立即检查其 type
值。不支持的浏览器将返回 text
,与 month 的回退行为相符。如果不支持 <input type="month">
,我们将隐藏原生选择器并显示作为回退的选择器 UI。
// 获取 UI 元素
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");
// 最初,隐藏回退元素
fallbackPicker.style.display = "none";
fallbackLabel.style.display = "none";
// 测试一个新的 date 输入框是否会回退至 text 输入框
const test = document.createElement("input");
try {
test.type = "month";
} catch (e) {
console.log(e.description);
}
// 如果回退了,运行 if 代码块中的代码
if (test.type === "text") {
// 隐藏原生选择器,显示回退元素
nativePicker.style.display = "none";
fallbackPicker.style.display = "block";
fallbackLabel.style.display = "block";
// 动态生成年份
// 月份总是相同的,故将它们硬编码
populateYears();
}
function populateYears() {
// 获取当前年份的数值表示
const date = new Date();
const year = date.getFullYear();
// 在年份 <select> 中,令该年和之前的 100 年可选
for (let i = 0; i <= 100; i++) {
const option = document.createElement("option");
option.textContent = year - i;
yearSelect.appendChild(option);
}
}
备注:请记住有些年份有 53 周(见每年的周数)!当你在开发产品应用时应当考虑这个问题。
规范
Specification |
---|
HTML Standard # month-state-(type=month) |
浏览器兼容性
BCD tables only load in the browser