30行 js 帮你理解日历是如何生成的
javascript的日期对象Date 是编程中非常常用的对象之一,这篇文章旨在帮助大家熟悉日期对象,并理解使用日期对象来创建一个日历。
言归正传
先看看简单的效果,后面还有复杂的
来看代码[javascript]
var weeks = "一二三四五六日".split(''),
monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
pastMleft ={
0:6,1:7,2:1,3:2,4:3,5:4,6:5
};
var time = new Date(),
thisY = time.getFullYear(),
thisM = time.getMonth(),
thisD = time.getDate();
function cal(){
var realM = thisM + 1,
firstDW = new Date(thisY,thisM,1).getDay(),
thisMD = monthDays[thisM],
pastMD = pastMleft[firstDW];
var lists = [];
monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD ; i++)
lists.push('<span class="past"></span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < 42-thisMD-pastMD ; i++)
lists.push('<span class="next"></span>')
$('#cal-wrap').html(lists.join(' '))
}
window.onload = cal;
这里是html
<div id="cal-wrap"></div>
这里是css样式
*{
margin:0;padding:0;
}
#cal-wrap{
width:280px;
background:#fff;
display: flex;
flex-wrap:wrap;
margin:50px auto;
}
#cal-wrap>span{
width:38px;
height:38px;
margin:1px;
line-height: 38px;
text-align: center;
color:#fff;
font-size: 12px;
}
#cal-wrap>.week{
color:#000;
}
.past,.next{
background: #ededed;
}
.now,.fur{
background:#5ea6e0;
}
.fur{
opacity: 0.5;
}
.today{
background: #ff7875;
}
下面来重点讲讲日历的生成过程
- 首先,日历表共有49个单元格,其中,周一到周日占据七个单元格,剩下的是上个月的末尾几天,本月份的天数,以及下个月的月初几天
- 所以第一步创建周一到周日,我们可以生成一个包含一到日的数组,后期遍历这个数组就能创建周一到周日
var weeks = "一二三四五六日".split('');//周一到周日
- 求上个月遗留的几天
根据这个推论,我们可以得到这样一个对象,当本月第一天是周一,上个月遗留七天;本月第一天周二上个月遗留一天…….本月第一天周日上个月遗留六天
var pastMleft ={
0:6,1:7,2:1,3:2,4:3,5:4,6:5
};
- 所以现在问题的关键变成了求本月的第一天是周几,看代码吧
var time = new Date(),
thisY = time.getFullYear(),//当前年份
thisM = time.getMonth(), //当前月份,返回0-11
realM = thisM + 1, //真实的月份,当前月份返回0时,真实应该是1月份
thisD = time.getDate(); //今天,返回今天所在的日期
var firstDW = new Date(thisY,thisM,1).getDay();//本月第一天是星期几,返回0-6,对应周日-周六
var pastMD = pastMleft[firstDW];//上个月遗留的天数
- 接下来求本月的天数,谚语有云:一三五七八十腊,三十一天永不差,所以我们得到了一个数组
var monthDays = [31,28,31,30,31,30,31,31,30,31,30,31];
- 然后要依据闰年或者平年的判断,来重置二月份的天数
monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
- 那么本月的天数也就出来了
var thisMD = monthDays[thisM];//本月天数
- 下个月初在本月的天数怎么求呢?
var nextMD = 49-7-pastMD-thisMD
- 现在日历的主体内容已经完成,就差动手创建元素了
var lists = [];
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)
lists.push('<span class="past"></span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < 42-pastMD-thisMD; i++)
lists.push('<span class="next"></span>')
$('#cal-wrap').html(lists.join(' '))
- 创建完成,看看网页里有没有我们创建的日历吧,最后把代码重置优化一下,将不变的变量放到一起声明复制,创建的过程放到一个函数里方便后期代码重复调用,就形成了我们之前的展示的代码
var weeks = "一二三四五六日".split(''),
monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
pastMleft ={
0:6,1:7,2:1,3:2,4:3,5:4,6:5
};
var time = new Date(),//默认以当前年月创建日历
thisY = time.getFullYear(),
thisM = time.getMonth(),
thisD = time.getDate();
function cal(){
var realM = thisM + 1,
firstDW = new Date(thisY,thisM,1).getDay(),
thisMD = monthDays[thisM],
pastMD = pastMleft[firstDW];
monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
var lists = [];
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD ; i++)
lists.push('<span class="past"></span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < 42-thisMD-pastMD ; i++)
lists.push('<span class="next"></span>')
$('#cal-wrap').html(lists.join(' '))
}
- 现在来想一个问题,想把上个月的日期和下个月的日期也填充进来,需要怎么做?首先我们要知道上个月是哪个月份
var pastM = thisM - 1;
pastM = pastM < 0?11:pastM;//上个月对应的月份
- 那么上个月最后一天是几号呢?就是上个月总共多少天
var pastM_lastD = monthDays[pastM];
- 然后来更改一下创建元素的代码
var lists = [];
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)//这里注意一下,我们创建span是从前往后,而上个月遗留日期是从后往前推算,所以要做个减法
lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < nextMD; i++)
lists.push('<span class="next">'+ (i+1) +'</span>')
$('#cal-wrap').html(lists.join(' '))
现在日历就比较齐全了,上个月,本月和下个月都有了,但是通常日历都有点击切换月份的效果,这个效果要怎么实现呢?首先我们先更改一下样式,看看添加了点击切换功能的日历
css样式表里先追加一段样式
.cal-title{
font-size: 14px;
display: flex;
justify-content: space-between;
padding: 5px 10px;
width: 100%;
background: #5ea6e0;
}
.cal-left-btn,.cal-right-btn{
cursor:pointer;
}
- 对应的html代码
<div id="cal-wrap">
<!-- <div class="cal-title">
<span class="cal-left-btn"><</span>
<span class="cal-title-content">2018-08</span>
<span class="cal-right-btn">></span>
</div> -->
</div>
- 先整理一下之前的cal,加入了上个月和下个月的日期,以及日历的头部
function cal(){
var pastM = thisM - 1,realM = thisM + 1;
var firstDW = new Date(thisY,thisM,1).getDay(),
thisMD = monthDays[thisM],
pastMD = pastMleft[firstDW];
nextMD = 42-thisMD-pastMD;
pastM = pastM < 0?11:pastM
var lists = [];
var pastM_lastD = monthDays[pastM];
lists.push(`<div class="cal-title">
<span class="cal-left-btn"><</span>
<span class="cal-title-content">${thisY}-${realM<10?0+''+realM:realM}</span>
<span class="cal-right-btn">></span>
</div>`)
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)
lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < nextMD; i++)
lists.push('<span class="next">'+ (i+1) +'</span>')
$('#cal-wrap').html(lists.join(' '))
}
- 理顺一下思路,上面代码已经让小伙伴们了解了日历是怎么创建出来的,其实创建日历做本质的两个变量就是年份和月份,也就是说,在点击事件当中,变化的是年份和月份,只要确定了年份和月份,再调用创建日历的代码就可以了,所以来写个点击事件吧
$('body')
.on('click','.cal-left-btn',function(){
thisM = thisM - 1;
if(thisM<0){
thisM = 11; thisY = thisY - 1
}
cal()
})
.on('click','.cal-right-btn',function(){
thisM = thisM + 1;
if(thisM>11){
thisM = 0; thisY = thisY + 1
}
cal()
})
- 上边的事件里,每点击一次,我们就让月份自增一次或者自减一次,同时月份超出本年的时候控制年份变化,然后在点击事件里调用创建日历的代码,就可以了,这里是完整的js代码
var weeks = "一二三四五六日".split(''),
monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
pastMleft ={
0:6,1:7,2:1,3:2,4:3,5:4,6:5
};
var time = new Date(),
thisY = time.getFullYear(),
thisM = time.getMonth(),
thisD = time.getDate();
function cal(){
monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
var pastM = thisM - 1,realM = thisM + 1;
var firstDW = new Date(thisY,thisM,1).getDay(),
thisMD = monthDays[thisM],
pastMD = pastMleft[firstDW];
nextMD = 42-thisMD-pastMD;
pastM = pastM < 0?11:pastM
var lists = [];
var pastM_lastD = monthDays[pastM];
lists.push(`<div class="cal-title">
<span class="cal-left-btn"><</span>
<span class="cal-title-content">${thisY}-${realM<10?0+''+realM:realM}</span>
<span class="cal-right-btn">></span>
</div>`)
for(var i=0,l=weeks.length; i<l; i++)
lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)
lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
for(var i=1; i <= thisMD; i++){
var str = i==thisD?'today':i<thisD?'now':'fur'
lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < nextMD; i++)
lists.push('<span class="next">'+ (i+1) +'</span>')
$('#cal-wrap').html(lists.join(' '))
}
$('body')
.on('click','.cal-left-btn',function(){
thisM = thisM - 1;
if(thisM<0){
thisM = 11; thisY = thisY - 1
}
cal()
})
.on('click','.cal-right-btn',function(){
thisM = thisM + 1;
if(thisM>11){
thisM = 0; thisY = thisY + 1
}
cal()
})
window.onload = cal;