• 微信
当前位置:首页 >> 建站学院 >> Web前端

【前端笔记】JavaScript实现日历

作者:玄北 时间:2022-02-08 阅读数:942人阅读

日历是网页上面常见的功能,一般开发,开发者就会借助第三方插件来解决这个效果。今天为大家介绍一下用纯生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);
}
JavaScript实现日历

此时我们会发现空出来了很多单元格,这个先不急,我们先来解决当前日期的选中样式,循环结束之后单元格的样式,以及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'
	}
}
JavaScript实现日历

通过观察我们发现当前日期已经添加了相关样式,以及空白的单元格也添加了相关的样式,只不过现在还看不出来。这时候我们就需要把空白处的单元格文本显示出来

为循环结束之后的单元格添加文本,这时候只需要显示下一个的日期即可,获取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();

此时所有单元格都显示完整了,具体的效果如下图:

JavaScript实现日历


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或者评论,玄北都会一一解答,希望能够帮助你。

转载请注明——本文源自【玄北博客】www.xuanbeiweb.cn

免责声明:本站部分文章、数据、图片来自互联网,

如果侵犯了你的权益请来信告知我们删除,否则不承担相应法律责任。邮箱:xuanbei@xuanbeiweb.cn

标签: JavaScript 特效 js
玄北头像

玄北

Hi~如果您正好看到这里,可以扫一扫微信二维码加我为好友,我是一个喜欢交朋友的人,我知道您也是哦~

玄北微信

发表评论:

评论记录:

暂无评论——欢迎您的点评!