1. 程式人生 > >30行 js 幫你理解日曆是如何生成的

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;