month 月份输入
<input type="month"> 是 HTML5 引入的月份选择控件,允许用户选择一个具体的年份和月份。该控件提交值格式为 YYYY-MM,适用于信用卡到期日、账单月份、报表周期等只需要精确到月份的场景。
前置知识
阅读本节前,建议先了解:datetime-local 日期时间输入
基础概念
什么是 month 输入
type="month" 输入框提供了一个年月组合选择界面。用户通过控件选择年份和月份,无需输入具体日期。主要特点:
- 年月一体化:年份和月份在同一个控件中选择
- 格式统一:提交值始终为
YYYY-MM格式 - 无需验证日期:不存在无效月份的问题(1-12 月)
- 紧凑界面:比分离的年份+月份下拉框更简洁
浏览器支持
| 浏览器 | 支持情况 | 控件外观 |
|---|---|---|
| Chrome | ✅ 完整支持 | 年月选择器 |
| Firefox | ✅ 支持(93+) | 年月输入框 |
| Safari | ✅ 支持(14.1+) | 年月滚轮 |
| Edge | ✅ 完整支持 | 年月选择器 |
| Opera | ✅ 完整支持 | 年月选择器 |
注意:在 Firefox 中,
type="month"的界面较简单,用户可能需要手动输入YYYY-MM格式。
提交值格式
YYYY-MM例如:2025-07 表示 2025 年 7 月。
语法
基本用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>month 月份输入示例</title>
</head>
<body>
<h2>选择月份</h2>
<form>
<label for="billing-month">账单月份:</label>
<input type="month" id="billing-month" name="billing_month">
<button type="submit">提交</button>
</form>
</body>
</html>设置默认值
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>month 默认值示例</title>
</head>
<body>
<h2>默认选中当前月份</h2>
<form>
<label for="current-month">当前月份:</label>
<!-- 默认值为当前年月 -->
<input type="month" id="current-month" name="current_month" value="2025-07">
<button type="submit">查询</button>
</form>
<script>
// 动态设置默认值为当前年月
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
document.getElementById('current-month').value = `${year}-${month}`;
</script>
</body>
</html>min 和 max 属性
限制可选月份范围:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>month 范围限制</title>
</head>
<body>
<h2>信用卡到期月份</h2>
<form>
<label for="expire-month">到期月份:</label>
<!-- 只能选择当前月份及以后的 60 个月内 -->
<input
type="month"
id="expire-month"
name="expire_month"
min="2025-07"
max="2030-07"
required
>
<button type="submit">确认</button>
</form>
</body>
</html>| 属性 | 说明 | 格式 |
|---|---|---|
min | 最早可选月份 | YYYY-MM |
max | 最晚可选月份 | YYYY-MM |
详细说明
step 步进控制
step 属性的单位为月,控制月份选择的粒度:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>month step 示例</title>
</head>
<body>
<h2>月份步进</h2>
<form>
<div>
<label for="monthly">每月可选(默认 step=1):</label>
<input type="month" id="monthly" name="monthly" step="1">
</div>
<div>
<label for="quarterly">每季度可选(step=3):</label>
<!-- 从 1 月开始,每 3 个月可选 -->
<input type="month" id="quarterly" name="quarterly" step="3" min="2025-01">
</div>
<div>
<label for="yearly">每年可选(step=12):</label>
<!-- 每年同一月份可选 -->
<input type="month" id="yearly" name="yearly" step="12" min="2025-01" value="2025-01">
</div>
</form>
</body>
</html>| step 值(月) | 含义 | 选择器行为 |
|---|---|---|
1(默认) | 每月可选 | 任意月份 |
3 | 每季度 | 1, 4, 7, 10 月 |
6 | 每半年 | 1, 7 月 |
12 | 每年 | 同一年月 |
any | 不限制 | 完全自由 |
valueAsDate 和 valueAsNumber
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>month 值属性</title>
</head>
<body>
<h2>获取月份值</h2>
<label for="my-month">选择月份:</label>
<input type="month" id="my-month" name="my_month" value="2025-07">
<div id="output"></div>
<script>
const monthInput = document.getElementById('my-month');
const output = document.getElementById('output');
monthInput.addEventListener('input', function () {
const str = this.value; // "2025-07"
const date = this.valueAsDate; // Date 对象(该月的第一天)
const num = this.valueAsNumber; // Unix 时间戳
if (date) {
output.innerHTML = `
<p>value: ${str}</p>
<p>年份: ${date.getFullYear()}年</p>
<p>月份: ${date.getMonth() + 1}月</p>
<p>该月第一天: ${date.toLocaleDateString('zh-CN')}</p>
<p>Unix 时间戳: ${num}</p>
`;
}
});
</script>
</body>
</html>JavaScript 计算月份范围
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>月份范围计算</title>
</head>
<body>
<h2>月份范围选择</h2>
<form>
<div>
<label for="start-month">开始月份:</label>
<input type="month" id="start-month" name="start_month" required>
</div>
<div>
<label for="end-month">结束月份:</label>
<input type="month" id="end-month" name="end_month" required>
</div>
<button type="button" id="calc-btn">计算月数</button>
</form>
<p id="result"></p>
<script>
const startMonth = document.getElementById('start-month');
const endMonth = document.getElementById('end-month');
// 设置开始月份的最小值和默认值为当前月
const now = new Date();
const currentYM = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
startMonth.min = currentYM;
startMonth.value = currentYM;
// 开始月份变化时更新结束月份
startMonth.addEventListener('change', function () {
endMonth.min = this.value;
if (endMonth.value && endMonth.value < this.value) {
endMonth.value = this.value;
}
});
// 计算月数差
document.getElementById('calc-btn').addEventListener('click', function () {
if (startMonth.value && endMonth.value) {
const [sy, sm] = startMonth.value.split('-').map(Number);
const [ey, em] = endMonth.value.split('-').map(Number);
const months = (ey - sy) * 12 + (em - sm);
document.getElementById('result').textContent =
`从 ${startMonth.value} 到 ${endMonth.value},共 ${months} 个月`;
}
});
</script>
</body>
</html>实战示例
信用卡到期日选择
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>信用卡到期日</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
max-width: 600px;
margin: 40px auto;
padding: 0 20px;
}
.form-group { margin-bottom: 16px; }
label { display: block; margin-bottom: 6px; font-weight: 600; }
input {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
input:focus { border-color: #27ae60; outline: none; }
.card-preview {
background: linear-gradient(135deg, #2c3e50, #3498db);
color: white;
padding: 24px;
border-radius: 12px;
margin-top: 20px;
}
.card-preview .label { color: rgba(255,255,255,0.7); font-size: 12px; }
.card-preview .value { font-size: 20px; font-weight: bold; margin-top: 4px; }
</style>
</head>
<body>
<h2>信用卡到期日</h2>
<form id="card-form">
<div class="form-group">
<label for="card-number">卡号:</label>
<input type="text" id="card-number" maxlength="19" placeholder="**** **** **** ****">
</div>
<div class="form-group">
<label for="expire-month">到期月份:</label>
<input type="month" id="expire-month" name="expire_month" required>
</div>
</form>
<div class="card-preview">
<div class="label">VALID THRU</div>
<div class="value" id="expire-display">--/--</div>
</div>
<script>
const expireInput = document.getElementById('expire-month');
const expireDisplay = document.getElementById('expire-display');
// 设置最小值为当前月份
const now = new Date();
const minMonth = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
expireInput.min = minMonth;
expireInput.addEventListener('input', function () {
if (this.value) {
const [year, month] = this.value.split('-');
expireDisplay.textContent = `${month}/${year.slice(2)}`;
}
});
</script>
</body>
</html>月度报表选择器
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>月度报表</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
max-width: 700px;
margin: 40px auto;
padding: 0 20px;
}
.controls { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
input[type="month"] {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
button {
padding: 8px 16px;
background: #2980b9;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
}
.report {
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
</style>
</head>
<body>
<h2>月度销售报表</h2>
<div class="controls">
<label for="report-month">报表月份:</label>
<input type="month" id="report-month" name="report_month">
<button type="button" id="gen-btn">生成报表</button>
</div>
<div class="report" id="report" style="display: none;">
<h3 id="report-title"></h3>
<table>
<thead>
<tr><th>指标</th><th>数值</th><th>环比</th></tr>
</thead>
<tbody>
<tr><td>总销售额</td><td id="sales">--</td><td id="sales-change">--</td></tr>
<tr><td>订单数</td><td id="orders">--</td><td id="orders-change">--</td></tr>
<tr><td>客单价</td><td id="avg">--</td><td id="avg-change">--</td></tr>
</tbody>
</table>
</div>
<script>
// 设置当前月份为默认值
const monthInput = document.getElementById('report-month');
const now = new Date();
monthInput.value = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;
document.getElementById('gen-btn').addEventListener('click', function () {
const [year, month] = monthInput.value.split('-').map(Number);
const monthNames = ['', '一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月'];
// 模拟数据生成
const seed = year * 12 + month;
const sales = Math.floor(50000 + (seed * 1234) % 80000);
const orders = Math.floor(200 + (seed * 567) % 500);
const avg = (sales / orders).toFixed(2);
document.getElementById('report').style.display = 'block';
document.getElementById('report-title').textContent =
`${year} 年 ${monthNames[month]} 销售报表`;
document.getElementById('sales').textContent = `¥${sales.toLocaleString()}`;
document.getElementById('orders').textContent = orders;
document.getElementById('avg').textContent = `¥${avg}`;
});
</script>
</body>
</html>注意事项
Firefox 中的特殊表现
Firefox 对 type="month" 的原生支持较晚加入,在较旧版本中可能显示为普通文本输入框。用户需要手动输入 YYYY-MM 格式。
valueAsDate 返回该月第一天
javascript
const monthInput = document.getElementById('my-month');
monthInput.value = '2025-07';
const date = monthInput.valueAsDate;
console.log(date.getDate()); // 1 — 返回该月第一天月份编号从 1 开始
与 JavaScript Date 对象的月份(0-11)不同,type="month" 的值中月份从 1 开始:
javascript
// type="month" 值: "2025-07" 表示 7 月
// JavaScript Date: getMonth() 返回 6 表示 7 月(从 0 开始)
const [year, month] = monthInput.value.split('-').map(Number);
const date = new Date(year, month - 1, 1); // 需要 month - 1不能限制到具体日期
type="month" 只能选择年月,无法精确到日期。如果需要选择某月的特定日期,应使用 type="date"。
最佳实践
- 配合说明文字:明确告知用户选择的格式和含义
- 合理设置范围:使用
min/max限制业务合理的月份范围 - 动态设置 min:对于"从现在起"的场景,用 JavaScript 设置最小值为当前月份
- 显示友好格式:将
YYYY-MM转换为2025年7月等可读格式展示 - 考虑降级方案:在不支持的浏览器中提供年份+月份两个下拉框
下一节
继续学习:week 周输入