如何创建一个功能正确的自定义年月选择器

本文详解如何构建一个轻量、无依赖的自定义年月选择器,重点解决初始化时年份显示错误(如默认显示2025而非2025)和当前年份误禁用等常见逻辑缺陷,通过调整初始化顺序与状态同步机制确保行为一致。

在前端开发中,原生 兼容性有限(如 Safari 旧版本不支持),而引入大型日期库又显得过度。因此,手写一个简洁可靠的自定义年月选择器是常见需求。但正如问题所示,看似简单的逻辑——“当前年份仅在所选月份早于当前月份时禁用”——极易因初始化时序错误导致异常行为。

核心问题在于:原始代码先调用 updateYearOptions(),此时

let selectedYear = new Date().getFullYear();

function updateYearOptions() {
  const now = new Date();
  const currentYear = now.getFullYear();
  const currentMonth = now.getMonth() + 1; // 1–12
  const selectedMonth = parseInt(document.getElementById('month').value, 10);

  const yearSelect = document.getElementById('year');
  const maxYear = currentYear + 20;
  yearSelect.innerHTML = '';

  // 默认选中年份:优先使用当前年,除非被禁用且用户未手动修改过
  let yearToSelect = currentYear;

  for (let year = currentYear; year <= maxYear; year++) {
    const option = document.createElement('option');
    option.value = year;
    option.textContent = year;

    // 禁用规则:仅当【是当前年】且【所选月份 < 当前月份】时禁用
    if (year === currentYear && selectedMonth < currentMonth) {
      option.disabled = true;
      // 若当前年被禁用,且尚未显式选择过其他年,则默认选下一年(避免空白)
      if (yearToSelect === currentYear) {
        yearToSelect = currentYear + 1;
      }
    }

    yearSelect.appendChild(option);
  }

  // 确保 yearSelect.value 是有效且未禁用的选项
  yearSelect.value = yearToSelect;
  selectedYear = yearToSelect;
  document.getElementById('selectedYear').value = yearToSelect;
}

function updateMonthOptions() {
  const monthSelect = document.getElementById('month');
  const currentMonth = new Date().getMonth() + 1;
  monthSelect.value = currentMonth; // ✅ 关键:先设 month 值
}

// ✅ 初始化顺序:先设月,再生成年选项
updateMonthOptions();
updateYearOptions();

function updateSelectedYear() {
  selectedYear = parseInt(document.getElementById('year').value, 10);
  document.getElementById('selectedYear').value = selectedYear;
}

? 关键注意事项:

  • 使用 parseInt(value, 10) 显式指定十进制,避免八进制解析风险;
  • updateYearOptions() 中的 yearToSelect 初始化为 currentYear,并在禁用时智能降级,比原逻辑更鲁棒;
  • 所有日期计算基于 new Date() 实时获取,确保跨年、跨月场景下逻辑准确;
  • 隐藏域 #selectedYear 用于表单提交,务必保持与 UI 同步。

此方案零依赖、语义清晰、可维护性强,适用于需要精细控制交互逻辑或兼容老旧浏览器的项目场景。