Skip to content

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"

最佳实践

  1. 配合说明文字:明确告知用户选择的格式和含义
  2. 合理设置范围:使用 min/max 限制业务合理的月份范围
  3. 动态设置 min:对于"从现在起"的场景,用 JavaScript 设置最小值为当前月份
  4. 显示友好格式:将 YYYY-MM 转换为 2025年7月 等可读格式展示
  5. 考虑降级方案:在不支持的浏览器中提供年份+月份两个下拉框

下一节

继续学习:week 周输入

参考链接