怎样用JavaScript实现日历组件?

来源:IPIPP.com作者:头衔:全栈工程师
导读:本期聚焦于小伙伴创作的《怎样用JavaScript实现日历组件?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《怎样用JavaScript实现日历组件?》有用,将其分享出去将是对创作者最好的鼓励。

用原生JavaScript实现日历组件,核心是通过Date对象处理日期相关计算,再结合DOM操作渲染视图,支持基础的上月、下月切换和日期选中功能。

怎样用JavaScript实现日历组件?

一、实现思路梳理

日历组件的核心逻辑可以分为以下几个部分:

  • 维护当前显示的年份和月份状态,默认初始化为当前系统日期
  • 计算当前月份的总天数,以及当月第一天是星期几,用于确定日历表格的起始位置
  • 生成6行7列的日历表格结构,填充上月的尾部日期、当月所有日期、下月的头部日期
  • 绑定上月、下月按钮的点击事件,更新状态并重新渲染日历
  • 绑定日期单元格的点击事件,实现选中状态切换

二、基础样式准备

先写基础的日历样式,保证布局清晰:

/* 日历容器 */
.calendar {
  width: 320px;
  border: 1px solid #e5e5e5;
  border-radius: 4px;
  padding: 16px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
/* 头部 */
.calendar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}
.calendar-header button {
  padding: 4px 12px;
  cursor: pointer;
  border: 1px solid #ddd;
  border-radius: 4px;
  background: #fff;
}
.calendar-header button:hover {
  background: #f5f5f5;
}
/* 星期行 */
.week-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  margin-bottom: 8px;
  color: #666;
  font-size: 14px;
}
/* 日期表格 */
.date-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: repeat(6, 1fr);
  gap: 4px;
}
.date-cell {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}
.date-cell.current-month {
  color: #333;
}
.date-cell.other-month {
  color: #ccc;
}
.date-cell.selected {
  background: #1890ff;
  color: #fff;
}
.date-cell:hover:not(.selected) {
  background: #f0f0f0;
}

三、JavaScript核心逻辑实现

接下来实现日历的核心功能代码:

class Calendar {
  constructor(containerId) {
    // 获取容器元素
    this.container = document.getElementById(containerId);
    // 初始化当前日期为系统当前日期
    const now = new Date();
    this.currentYear = now.getFullYear();
    this.currentMonth = now.getMonth(); // 0-11 表示1-12月
    this.selectedDate = null; // 选中的日期
    this.init();
  }

  // 初始化组件
  init() {
    this.render();
  }

  // 渲染整个日历
  render() {
    this.container.innerHTML = '';
    // 渲染头部
    const header = this.renderHeader();
    // 渲染星期行
    const weekRow = this.renderWeekRow();
    // 渲染日期网格
    const dateGrid = this.renderDateGrid();
    this.container.appendChild(header);
    this.container.appendChild(weekRow);
    this.container.appendChild(dateGrid);
  }

  // 渲染头部:显示年月+切换按钮
  renderHeader() {
    const header = document.createElement('div');
    header.className = 'calendar-header';
    const prevBtn = document.createElement('button');
    prevBtn.textContent = '上月';
    prevBtn.addEventListener('click', () => this.switchMonth(-1));
    const nextBtn = document.createElement('button');
    nextBtn.textContent = '下月';
    nextBtn.addEventListener('click', () => this.switchMonth(1));
    const title = document.createElement('div');
    title.textContent = `${this.currentYear}年${this.currentMonth + 1}月`;
    header.appendChild(prevBtn);
    header.appendChild(title);
    header.appendChild(nextBtn);
    return header;
  }

  // 渲染星期行
  renderWeekRow() {
    const weekRow = document.createElement('div');
    weekRow.className = 'week-row';
    const weeks = ['日', '一', '二', '三', '四', '五', '六'];
    weeks.forEach(week => {
      const cell = document.createElement('div');
      cell.textContent = week;
      weekRow.appendChild(cell);
    });
    return weekRow;
  }

  // 渲染日期网格
  renderDateGrid() {
    const dateGrid = document.createElement('div');
    dateGrid.className = 'date-grid';
    // 获取当月第一天是星期几
    const firstDay = new Date(this.currentYear, this.currentMonth, 1).getDay();
    // 获取当月总天数
    const currentMonthDays = new Date(this.currentYear, this.currentMonth + 1, 0).getDate();
    // 获取上月总天数
    const prevMonthDays = new Date(this.currentYear, this.currentMonth, 0).getDate();
    // 生成所有日期
    const dates = [];
    // 上月尾部日期
    for (let i = firstDay - 1; i >= 0; i--) {
      dates.push({
        day: prevMonthDays - i,
        month: this.currentMonth - 1,
        year: this.currentMonth === 0 ? this.currentYear - 1 : this.currentYear,
        isCurrentMonth: false
      });
    }
    // 当月所有日期
    for (let i = 1; i <= currentMonthDays; i++) {
      dates.push({
        day: i,
        month: this.currentMonth,
        year: this.currentYear,
        isCurrentMonth: true
      });
    }
    // 下月头部日期,凑满42个单元格(6行7列)
    const remainCount = 42 - dates.length;
    for (let i = 1; i <= remainCount; i++) {
      dates.push({
        day: i,
        month: this.currentMonth + 1,
        year: this.currentMonth === 11 ? this.currentYear + 1 : this.currentYear,
        isCurrentMonth: false
      });
    }
    // 生成日期单元格
    dates.forEach(dateItem => {
      const cell = document.createElement('div');
      cell.className = 'date-cell';
      if (dateItem.isCurrentMonth) {
        cell.classList.add('current-month');
      } else {
        cell.classList.add('other-month');
      }
      // 判断是否为选中日期
      if (this.selectedDate && 
          this.selectedDate.year === dateItem.year &&
          this.selectedDate.month === dateItem.month &&
          this.selectedDate.day === dateItem.day) {
        cell.classList.add('selected');
      }
      cell.textContent = dateItem.day;
      // 绑定点击选中事件
      cell.addEventListener('click', () => {
        this.selectedDate = dateItem;
        this.render();
      });
      dateGrid.appendChild(cell);
    });
    return dateGrid;
  }

  // 切换月份
  switchMonth(step) {
    this.currentMonth += step;
    // 处理月份越界
    if (this.currentMonth > 11) {
      this.currentMonth = 0;
      this.currentYear += 1;
    } else if (this.currentMonth < 0) {
      this.currentMonth = 11;
      this.currentYear -= 1;
    }
    this.render();
  }
}

// 初始化日历,容器id为calendar-container
new Calendar('calendar-container');

四、使用示例

在HTML中只需要准备一个容器元素,引入样式和JS代码即可运行:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>原生JS日历组件</title>
  <style>
    /* 这里放入上面的CSS样式代码 */
  </style>
</head>
<body>
  <div id="calendar-container" class="calendar"></div>
  <script>
    /* 这里放入上面的JavaScript代码 */
  </script>
</body>
</html>

五、功能扩展建议

上述代码实现了日历的基础功能,还可以根据需求扩展更多能力:

  • 添加今天按钮,快速跳转到当前日期
  • 支持标记特殊日期,比如 holidays 数组传入需要标记的日期,渲染时添加特殊样式
  • 添加日期范围选择功能,支持选中开始和结束两个日期
  • 增加月份快速选择器,支持直接选择年份和月份

整个实现过程没有依赖任何第三方库,核心逻辑围绕Date对象的日期计算和DOM操作展开,理解后可以灵活调整适配不同的业务需求。

JavaScript日历组件Date对象前端开发DOM操作修改时间:2026-06-04 03:33:01

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。