引言
在 Web 开发中,日历组件是一个常见且实用的功能,它不仅能方便用户查看日期,还能结合任务安排,让用户清晰地了解每个日期对应的任务。本文将详细介绍如何使用 JavaScript 实现一个带任务显示的日历,同时结合 sessionStorage
存储任务数据,通过点击按钮实现月份切换和返回今日等功能。
功能概述
我们要实现的日历具有以下功能:
- 显示当前月份的日历,包括上个月的部分日期和本月的所有日期。
- 标记当前日期,使其与其他日期区分开来。
- 根据
sessionStorage
中存储的任务数据,在相应日期上显示任务信息,包括任务开始、进行中和结束的状态。 - 提供切换到上一个月、下一个月和返回今日的功能。
项目效果:
代码实现步骤
(一)HTML 结构搭建
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="./css/日历.css" />
</head>
<body>
<div id="top_box">
<span class="font">321321</span>
<div class="left">
<div class="left_arrow" onclick="last()">
<img src="./img/prev.png" alt="" />
</div>
</div>
<div class="right">
<div id="today" onclick="today()">今天</div>
<div class="right_arrow" onclick="next()"><img src="./img/right.png" alt="" /></div>
</div>
</div>
<div id="box">
<div class="table_head">
<div class="columns">周一</div>
<div class="columns">周二</div>
<div class="columns">周三</div>
<div class="columns">周四</div>
<div class="columns">周五</div>
<div class="columns">周六</div>
<div class="columns">周日</div>
</div>
<div id="content"></div>
</div>
<script src="js/日历.js"></script>
</body>
</html>
详细解释
- 顶部导航栏(
#top_box
):这部分是整个日历的控制区域,包含了几个重要的元素。span.font
用于显示当前月份的标题,让用户明确当前查看的时间范围。div.left
中的div.left_arrow
是切换到上一个月的按钮,通过点击该按钮,用户可以查看上一个月的日历。div.right
包含了两个按钮,div#today
用于返回今日,点击后日历会显示当前日期所在的月份;div.right_arrow
则是切换到下一个月的按钮,方便用户查看未来月份的日历。 - 日历表格头部(
.table_head
):该区域显示了一周七天的名称,即周一到周日,为用户提供了清晰的日期参照。 - 日历内容区域(
#content
):这是日历的核心显示区域,用于动态显示日历的日期以及对应的任务信息。后续的 JavaScript 代码会将生成的 HTML 内容插入到这个区域中。
(二)JavaScript 逻辑实现
1. 初始化和数据存储
// 获取当前日期
let now = new Date();
render();
// 将数据存储到 sessionStorage 中
sessionStorage.setItem('data',
'[{"title":"任务分割","start":"2025-04-15","end":"2025-04-31"},{"title":"1111","start":"2025-01-18","end":"2025-01-20"},{"title":"任务222222分割","start":"2025-01-21","end":"2025-01-31"},{"title":"任务3333333分割","start":"2025-01-25","end":"2025-01-31"}]'
);
详细解释
let now = new Date();
:创建一个Date
对象,获取当前的日期和时间。这个对象将作为后续日期计算和显示的基础。render();
:调用render
函数,开始进行日历的渲染操作,将当前月份的日历和任务信息显示在页面上。sessionStorage.setItem('data', ...)
:使用sessionStorage
来存储任务数据。sessionStorage
是 HTML5 新增的一个会话存储对象,它允许在浏览器会话期间临时保存数据。这里将一个包含多个任务信息的 JSON 字符串存储在data
键下,每个任务信息包含任务标题、开始日期和结束日期。
2. 日历渲染函数 render
function render() {
//获取到上面存的数据
let data = JSON.parse(sessionStorage.getItem('data'));
console.log(data);
// 获取月
let year = now.getFullYear();
// 获取年
let month = now.getMonth() + 1;
// 获取当前月的总天数
let self_dates = new Date(year, month, 0).getDate();
// 获取当前月第一天是星期几
let firstDay = new Date(`${year}-${month}-1`).getDay();
// 生成标题字符串
let date_str = `${month}月 ${year}`;
// 生成好的字符串更新到标题容器中
document.getElementsByClassName('font')[0].innerHTML = date_str;
let str = '';
// 如果当前月第一天不是周一的话
if (firstDay != 1) {
// 获取上个月的月份
let last_month = now.getMonth() - 1 < 0 ? 12 : now.getMonth() + 1;
// 获取上一个月的年份
let last_year = month == 1 ? year - 1 : year;
// 获取上个月的总天数
let last_dates = new Date(last_year, last_month, 0).getDate();
//填充上一个月的日期
for (let i = 0; i < firstDay - 1; i++) {
str = `
<div class="items" style="color:gray;">
<div class="date">${last_dates}</div>
</div>
` + str;
last_dates--;
}
}
// 填充当前月的日期
for (let i = 0; i < self_dates; i++) {
str += `<div class="items ${(function(){
let now = new Date();
if(now.getFullYear() == year && now.getMonth()+1 == month && now.getDate() == i+1){
return 'light'
}else{
return '';
}
}())}">
<div class="date">${i+1}</div>`;
// 遍历数据
for (let j in data) {
// 获取数据开始的日期
let start = +new Date(data[j].start);
// 获取数据结束的日期
let end = +new Date(data[j].end);
// 获取当前的日期
let nows = +new Date(`${year}-${month<10?'0'+month:month}-${i+1}`);
console.log(start, data[j].start, nows, `${year}-${month}-${i+1}`, start == nows, j);
// 如果现在是在开始和结束时间段内
if (nows <= end && nows >= start) {
// 如果现在是开始日期
if (nows == start) {
str += `<div class="plan">${data[j].title}</div>`;
// 如果现在是结束日期
} else if (nows == end) {
str += `<div class="plan">》》》》》</div>`;
//都不是的华
} else {
str += `<div class="plan"></div>`;
}
}
}
str += `</div>`;
}
// 更新日历内容
document.getElementById('content').innerHTML = str;
}
详细解释
- 数据获取:
let data = JSON.parse(sessionStorage.getItem('data'));
:从sessionStorage
中获取之前存储的任务数据,并使用JSON.parse
方法将 JSON 字符串转换为 JavaScript 对象。
- 日期信息计算:
let year = now.getFullYear();
和let month = now.getMonth() + 1;
:分别获取当前日期的年份和月份。需要注意的是,getMonth()
方法返回的月份是从 0 开始的,所以要加 1 才能得到实际的月份。let self_dates = new Date(year, month, 0).getDate();
:通过创建一个新的Date
对象,传入年份、月份和 0 作为日期参数,获取当前月的总天数。因为传入 0 表示上个月的最后一天,所以可以得到当前月的总天数。let firstDay = new Date(
\({year}-\){month}-1).getDay();
:创建一个表示当前月第一天的Date
对象,并使用getDay()
方法获取这一天是星期几。返回值 0 表示周日,1 表示周一,以此类推。
- 标题更新:
let date_str =
${month}月 ${year};
:生成当前月份的标题字符串。document.getElementsByClassName('font')[0].innerHTML = date_str;
:将生成的标题字符串更新到页面上的span.font
元素中。
- 上个月日期填充:
- 如果当前月第一天不是周一,需要填充上个月的部分日期。通过计算上个月的月份和年份,获取上个月的总天数,然后使用循环生成 HTML 字符串,并将其添加到
str
变量的前面。这些日期的颜色设置为灰色,以与当前月的日期区分开来。
- 如果当前月第一天不是周一,需要填充上个月的部分日期。通过计算上个月的月份和年份,获取上个月的总天数,然后使用循环生成 HTML 字符串,并将其添加到
- 当前月日期填充:
- 使用循环遍历当前月的每一天,生成对应的 HTML 元素。对于当前日期,通过一个立即执行函数判断是否为当前日期,如果是则添加
light
类,用于特殊标记。 - 对于每一天,遍历任务数据,判断该日期是否在某个任务的开始和结束日期范围内。如果是,则根据具体情况显示任务标题、结束标识或空占位符。
- 使用循环遍历当前月的每一天,生成对应的 HTML 元素。对于当前日期,通过一个立即执行函数判断是否为当前日期,如果是则添加
- 内容更新:
document.getElementById('content').innerHTML = str;
:将生成的 HTML 字符串插入到页面的#content
元素中,完成日历的渲染。
3. 月份切换和返回今日功能
function next() {
now.setMonth(now.getMonth() + 1);
render();
}
function last() {
now.setMonth(now.getMonth() - 1);
render();
}
function today() {
now = new Date();
render();
}
详细解释
next
函数:将now
对象的月份加 1,然后调用render
函数重新渲染日历,实现切换到下一个月的功能。last
函数:将now
对象的月份减 1,再调用render
函数重新渲染日历,实现切换到上一个月的功能。today
函数:将now
对象重置为当前日期,然后调用render
函数重新渲染日历,实现返回今日的功能。
四、代码优化与拓展建议
- 数据动态加载:目前任务数据是硬编码在
sessionStorage
中的,可以将其改为从后端接口获取,实现数据的动态更新。例如,使用fetch
或XMLHttpRequest
发送请求,获取最新的任务数据。 - 样式优化:进一步优化日历的样式,添加更多的动画效果和交互反馈,提升用户体验。比如,在日期上添加鼠标悬停效果,显示更多的任务详情。
- 性能优化:当任务数据量较大时,频繁的 DOM 操作可能会影响性能。可以考虑使用虚拟列表等技术,减少不必要的 DOM 渲染。
五、总结
通过以上详细的代码实现和分析,我们成功使用 JavaScript 实现了一个功能丰富的带任务显示的日历。该日历不仅能够准确显示日期和任务信息,还提供了方便的交互操作,满足了用户在不同时间范围查看和管理任务的需求。在实际项目中,可以根据具体需求对代码进行进一步的优化和拓展,使其更加完善和实用。