30行 js 幫你理解日曆是如何生成的
阿新 • • 發佈:2018-12-27
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;