【前端笔记】JavaScript实现日历
日历是网页上面常见的功能,一般开发,开发者就会借助第三方插件来解决这个效果。今天为大家介绍一下用纯生JS实现日历功能,项目演示地址:http://jsrlgn.monanjz.top
1. HTML+CSS
布局方式采用table表格方式,th标签生成7个,用作显示礼拜几,生成42个td标签,用来显示某一天是几号,css这里就不做过多讲解,代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"> <title>JS日历功能</title> <style type="text/css"> * { margin: 0; padding: 0; } .calendar { width: 780px; margin: 100px auto; border: 1px solid #4E6EF2; border-radius: 5px; box-shadow: 3px 3px 5px rgba(0,0,0,.1); } .calendarWarp { padding: 1rem; } .calendarHd { padding-bottom: 15px; border-bottom: 1px solid #ccc; display: flex; justify-content: space-between; } .calendarBtn span { display: inline-block; width: 68px; height: 28px; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; border-left: 1px solid #ccc; text-align: center; line-height: 28px; font-size: 14px; margin-left: -9px; cursor: pointer; transition: all .3s ease; } .calendarBtn span:hover { background-color: #ECF5FF; } .calendarBtn span:first-child { border-radius: 3px 0 0 0; } .calendarBtn span:last-child { border-right: 1px solid #ccc; border-radius: 0 3px 3px 0; } .calendarBd { margin-top: 10px; } .table { width: 100%; border-collapse:collapse; } .table thead { height: 50px; } .table tbody { margin-top: 10px; } .table tr td { width: 112px; height: 87px; border: 1px solid #e9e9e9; text-align: center; font-weight: bold; cursor: pointer; } .table tr td:hover { background-color: #F2F8FE; } .table tr td.prev,.table tr td.next { color: #ccc; } .currentTody { background-color: #F2F8FE; color: #0080FF; } @media ( max-width: 768px ) { .calendar { width: 95%; } .table tr td { height: 65px; } } </style> </head> <body> <div class="calendar"> <div class="calendarWarp"> <div class="calendarHd"> <div class="calendarYearMonth" id="yearMonth">1997年8月</div> <div class="calendarBtn"> <span id="prevMonth">上个月</span> <span id="toDayBtn">今天</span> <span id="nextMonth">下个月</span> </div> </div> <div class="calendarBd"> <table class="table"> <thead> <th>一</th> <th>二</th> <th>三</th> <th>四</th> <th>五</th> <th>六</th> <th>日</th> </thead> <tbody id="tbody"> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> </tbody> </table> </div> </div> </div></body></html>
这里采用html写死的方式,觉得html代码过于臃肿,可以采用JS生成标签的方式。
2. JS部分
2.1 获取到相关的元素
// 获取显示年月日的元素 var yearMonth = document.getElementById('yearMonth'); // 获取下个月按钮 var nextMonthBtn = document.getElementById('nextMonth'); // 获取上个月按钮 var prevMonthBtn = document.getElementById('prevMonth'); // 表格主体 var tbody = document.getElementById('tbody');
2.2 通过new Date()获取到当前日期
// 当前时间 var date = new Date(); // 当前年份 var year = date.getFullYear(); // 当前日 var day = date.getDate(); // 当前月,从0开始计算月份的 var month = date.getMonth() + 1;
2.3 显示当前的年月日
// 更新头部数据 yearMonth.innerHTML = year + '年' + month + '月' + day + '日';
2.4 循环遍历添加单元格文本
首先我们要确定每个月1号要显示的文本在哪一个单元格,我们可以通过礼拜几来判断,打个比方:2022年1月1日,这一天是礼拜六,那么就是从第一行的第六个位置开始循环,1月31天,那么就会循环到第36个单元格,具体代码和效果如下:
// 获取所有的 tdvar tds = document.getElementsByTagName('td'); // 获取当月有多少天 var havaDay = new Date(year,month,0).getDate(); // 获取当月1日,是周几 var monthDayISWeek = new Date(year,month-1,1).getDay(); // 如果当前是周日,那么 monthDayISWeek=7monthDayISWeek = monthDayISWeek == 0 ? 7 : monthDayISWeek; // 循环遍历添加单元格文本 // 从1号是周几开始循环,循环到一个月有多少条+monthDayISWeek for ( var i = monthDayISWeek; i < havaDay+monthDayISWeek;i++ ) { // tds[i-1] 因为单元格是从0开始的,所以要减一。 // 显示的文本=当前循环的数字-monthDayISWeek+1,举个例子:6-6+1=1,这个地方要自己反复的理解 tds[i-1].innerHTML = i-monthDayISWeek+1; // 为每一个单元格设置自定义属性 tds[i-1].setAttribute('data-date',i-monthDayISWeek+1); }
此时我们会发现空出来了很多单元格,这个先不急,我们先来解决当前日期的选中样式,循环结束之后单元格的样式,以及1号之前单元格样式。
// 添加单元格相关样式 for ( var j = 0 ; j < tds.length ; j++ ) { // 如果当前项小于monthDayISWeek就添加prev样式 if ( j < monthDayISWeek-1) { tds[j].className = 'prev' } // 为当前日期添加选中样式 if (tds[j].getAttribute('data-date') == day) { tds[j].className = 'currentTody' } // 如果当前项大于本月的最一天就添加next样式 if ( j >= havaDay+monthDayISWeek-1) { tds[j].className = 'next' } }
通过观察我们发现当前日期已经添加了相关样式,以及空白的单元格也添加了相关的样式,只不过现在还看不出来。这时候我们就需要把空白处的单元格文本显示出来
为循环结束之后的单元格添加文本,这时候只需要显示下一个的日期即可,获取className为next的所有元素,循环遍历,添加文本。
// 为next样式的单元格添加文本 var nextDs = document.getElementsByClassName('next');for ( var k = 0 ; k < nextDs.length ; k++ ) { nextDs[k].innerHTML = k+1; }
为1号之前的单元格添加相关文本
function preText () { // 如果当前月1份,那么上一月就是12月,否则就减1 var prevMonth = month == 1 ? 12 : month - 1; // 如果当前月1份,那么上一月就是上一年12月,否则就为当前年份 var prevYear = month == 1 ? year - 1 : year; // 上一个月有多少天 var prevMonthHavaDay = new Date(prevYear,prevMonth,0).getDate(); // 获取prev样式的单元格 var prevtDs = document.getElementsByClassName('prev'); for ( let i = prevtDs.length-1 ; i >= 0 ; i-- ) { prevtDs[i].innerHTML = prevMonthHavaDay--; } } preText();
此时所有单元格都显示完整了,具体的效果如下图:
2.5 注册相关事件
注册事件之前呢,我们需要将上面的一部分代码封装到函数当中,发生事件,就直接执行函数即可。具体函数代码如下:
// 初始化日历 calendar(year,month,day); function calendar (year,month,day) { // 更新头部数据 yearMonth.innerHTML = year + '年' + month + '月' + day + '日'; // 获取所有的td var tds = document.getElementsByTagName('td'); // 获取当月有多少天 var havaDay = new Date(year,month,0).getDate(); // 获取当月1日,是周几 var monthDayISWeek = new Date(year,month-1,1).getDay(); // 如果当前是周日,那么monthDayISWeek=7 monthDayISWeek = monthDayISWeek == 0 ? 7 : monthDayISWeek; // 循环遍历添加单元格文本 // 从1号是周几开始循环,循环到31+monthDayISWeek for ( var i = monthDayISWeek; i < havaDay+monthDayISWeek;i++ ) { // tds[i-1] 因为单元格是从0开始的,所以要减一。 // 显示的文本=当前循环的数字-monthDayISWeek+1,举个例子:6-6+1=1,这个地方要自己反复的理解 tds[i-1].innerHTML = i-monthDayISWeek+1; // 为每一个单元格设置自定义属性 tds[i-1].setAttribute('data-date',i-monthDayISWeek+1); } // 添加单元格相关样式 for ( var j = 0 ; j < tds.length ; j++ ) { // 如果当前项小于monthDayISWeek就添加prev样式 if ( j < monthDayISWeek-1) { tds[j].className = 'prev' } // 为当前日期添加选中样式 if (tds[j].getAttribute('data-date') == day) { tds[j].className = 'currentTody' } // 如果当前项大于本月的最一天就添加next样式 if ( j >= havaDay+monthDayISWeek-1) { tds[j].className = 'next' } } // 为next样式的单元格添加文本 var nextDs = document.getElementsByClassName('next'); for ( var k = 0 ; k < nextDs.length ; k++ ) { nextDs[k].innerHTML = k+1; } // 为prev样式的单元格添加文本 function preText () { // 如果当前月1份,那么上一月就是12月,否则就减1 var prevMonth = month == 1 ? 12 : month - 1; // 如果当前月1份,那么上一月就是上一年12月,否则就为当前年份 var prevYear = month == 1 ? year - 1 : year; // 上一个月有多少天 var prevMonthHavaDay = new Date(prevYear,prevMonth,0).getDate(); // 获取prev样式的单元格 var prevtDs = document.getElementsByClassName('prev'); for ( let i = prevtDs.length-1 ; i >= 0 ; i-- ) { prevtDs[i].innerHTML = prevMonthHavaDay--; } } preText(); }
为下一个月按钮注册事件,这里为什么要新建一个函数呢?因为当我们点击className为next时,也会跳转到下一个月
// 点击下个月按钮 nextMonthBtn.addEventListener('click',clickNextMonth); // 下一个月函数 function clickNextMonth () { // 如果月份整除12等于0,那么就让月份变为1,年份+1 if ( month % 12 == 0 ) { month = 1; year++; } else { month++; } // 更新数据 calendar(year,month,1); }
此时我们会发现一个错误,就是点击下一个月会出现样式混乱,当前月的日期也显示为灰色,这时候我们只需在calendar函数开始,清除所有单元格的样式即可。
// 清除所有td的样式与文本 for ( let a = 0 ; a < tds.length ; a++ ) { tds[a].innerHTML = ''; tds[a].removeAttribute('data-date'); tds[a].className = '';}
点击上一个月按钮
// 点击上个月按钮 prevMonthBtn.addEventListener('click',clickPreveMonth); // 上一个月函数 function clickPreveMonth () { // 如果当前月份等于1,点击了就让月份变为12,年份-1 if (month == 1) { month = 12; year--; } else { month--; } // 更新数据 calendar(year,month,1); }
点击今天按钮
// 点击今天按钮 toDayBtn.addEventListener('click',function () { // 当前时间 var newDate = new Date(); // 当前年份 year = newDate.getFullYear(); // 当前日 day = newDate.getDate(); // 当前月,从0开始计算月份的 month = newDate.getMonth() + 1; calendar(year,month,day); })
接下来为类名为pre和next注册事件,这个事件需要在calendar函数体内,为所有样式的单元格添加文本那个循环体加,如下:
// 为next样式的单元格添加文本 var nextDs = document.getElementsByClassName('next');for ( var k = 0 ; k < nextDs.length ; k++ ) { nextDs[k].innerHTML = k+1; // 为下一个月的每个单元格添加点击事件 nextDs[k].addEventListener('click',clickNextMonth); }
// 获取prev样式的单元格 var prevtDs = document.getElementsByClassName('prev');for ( let i = prevtDs.length-1 ; i >= 0 ; i-- ) { prevtDs[i].innerHTML = prevMonthHavaDay--; // 为上一个月的每个单元格添加点击事件 prevtDs[i].addEventListener('click',clickPreveMonth); }
此时所有的功能都已经完善了,需要源码的童鞋可以查看完整项目地址:http://jsrlgn.monanjz.top
有任何疑问可以加v或者评论,玄北都会一一解答,希望能够帮助你。
免责声明:本站部分文章、数据、图片来自互联网,
如果侵犯了你的权益请来信告知我们删除,否则不承担相应法律责任。邮箱:xuanbei@xuanbeiweb.cn
上一篇:前端灰度发布是什么?
下一篇:js网页禁止右键下载代码