原生Js實現一個日歷組件

  • 日歷組件圖示如下:


    calendar.PNG
  • 結構代碼如下:
      <div id="calendar">
            <!-- input輸入框 -->
            <!--<div class="chooseDate">-->
                <!--<input type="text" placeholder="點擊選擇時間">-->
                <!--<i class="iconfont icon-rili"></i>-->
            <!--</div>-->

            <!-- 組件主體 -->
            <!--<div class="calendar clear">-->

                <!-- 日歷主體左側 -->
                <!--<div class="c-show">-->

                    <!-- 左側展示日期的部分 -->
                    <!--<a class="year" href="">2017</a>-->
                    <!--<a class="week" href="">星期一</a>-->
                    <!--<a class="day" href="">4月24日</a>-->

                    <!-- 點擊左側年份,可展示選擇年份的列表和選擇月份的按鈕 -->
                    <!--<a class="chooseMonth" href="">Choose Month</a>-->
                    <!--<div class="yearList">-->
                        <!--<a class="iconfont" href="">
                            <i class="iconfont icon-Shang"></i>
                        </a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a href="" class="year-item">2017</a>-->
                        <!--<a class="iconfont" href="">
                            <i class="iconfont icon-xia"></i>
                        </a>-->
                    <!--</div>-->

                    <!-- 點擊選擇月份,可展示月份列表和選擇年份按鈕 -->
                    <!--<a class="chooseYear" href="">Choose Year</a>-->
                    <!--<div class="monthList">-->
                        <!--<a href="" class="month-item">January</a>-->
                        <!--<a href="" class="month-item">Feberraut</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                        <!--<a href="" class="month-item">Jan</a>-->
                    <!--</div>-->
                <!--</div>-->

                <!-- 日歷主體右側 -->
                <!--<div class="c-box">-->

                    <!-- 日歷主體右側年月title -->
                    <!--<div class="c-year clear">-->
                        <!--<i class="iconfont icon-zuo"></i>-->
                        <!--<a class="years" href="">2017/4</a>-->
                        <!--<i class="iconfont icon-gengduo"></i>-->
                    <!--</div>-->

                    <!-- 日歷主體右側,展示一整月的日期 -->
                    <!--<table class="date-box">-->
                        <!--<thead>-->
                            <!--<tr>-->
                                <!--<th>一</th>-->
                                <!--<th>二</th>-->
                                <!--<th>三</th>-->
                                <!--<th>四</th>-->
                                <!--<th>五</th>-->
                                <!--<th>六</th>-->
                                <!--<th>日</th>-->
                            <!--</tr>-->
                        <!--</thead>-->
                        <!--<tbody>-->
                            <!--<tr>-->
                                <!--<td><a class="active">13</a></td>-->
                                <!--<td><a>23</a></td>-->
                                <!--<td><a>3</a></td>-->
                                <!--<td>4</td>-->
                                <!--<td>5</td>-->
                                <!--<td>6</td>-->
                                <!--<td>7</td>-->
                            <!--</tr>-->
                            <!--<tr>-->
                                <!--<td>1</td>-->
                                <!--<td>2</td>-->
                                <!--<td>3</td>-->
                                <!--<td>4</td>-->
                                <!--<td>5</td>-->
                                <!--<td>6</td>-->
                                <!--<td>7</td>-->
                            <!--</tr>-->
                            <!--<tr>-->
                                <!--<td>1</td>-->
                                <!--<td>2</td>-->
                                <!--<td>3</td>-->
                                <!--<td>4</td>-->
                                <!--<td>5</td>-->
                                <!--<td>6</td>-->
                                <!--<td>7</td>-->
                            <!--</tr>-->
                            <!--<tr>-->
                                <!--<td>1</td>-->
                                <!--<td>2</td>-->
                                <!--<td>3</td>-->
                                <!--<td>4</td>-->
                                <!--<td>5</td>-->
                                <!--<td>6</td>-->
                                <!--<td>7</td>-->
                            <!--</tr>-->
                            <!--<tr>-->
                                <!--<td>1</td>-->
                                <!--<td>2</td>-->
                                <!--<td>3</td>-->
                                <!--<td>4</td>-->
                                <!--<td>5</td>-->
                                <!--<td>6</td>-->
                                <!--<td>7</td>-->
                            <!--</tr>-->
                        <!--</tbody>-->
                    <!--</table>-->

                    <!-- 日歷主體右側底部按鈕 -->
                    <!--<div class="c-button">-->
                        <!--<a class="go-determine" href="">確定</a>-->
                        <!--<a class="go-today" href="">今天</a>-->
                    <!--</div>-->
                <!--</div>-->
            <!--</div>-->
        </div>
  • 開發過程如下:
    1.通過class定義一個Calendar類,綁定相應屬性
    class Calendar {
    
      //初始化
      constructor(node){
          this.calendarMain = node;            //傳遞實例
          this.calendarFound = false;         //日歷未創建
          this.display = false;               //日歷未顯示
          this.date = new Date();
          this.calendarDate = {};
      }
    
    2.因為操作dom有很多重復使用的方法,因此我們先封裝一部分常用方法
      //創建節點
      creatElement(tag){
          return document.createElement(tag);
      }
    
      //選擇節點
      queryElement(selector,boolean){
          return boolean ? document.querySelector(selector) : document.querySelectorAll(selector);
      }
    
      //隱藏元素
      hide(elm){
          elm.style.display = 'none';
      }
    
      //顯示元素
      show(elm){
          elm.style.display = '';
      }
    
    3.獲取dom節點,創建一個實例,并調用start方法展示 input
    var calendarUnit = document.getElementById('calendar');
    var myCalendar = new Calendar(calendarUnit);
    
    myCalendar.start();
    
    4.所有的方法都寫在Calendar類里,綁定在類的prototype屬性上
      //開始繪制input
      start(){
    
          var cthis = this;               //cthis指向Calendar
    
          //繪制chooseDate
          var chooseDate = this.creatElement('div');
          chooseDate.className = 'chooseDate';
          var input = this.creatElement('input');
          input.setAttribute('type','text');
          input.setAttribute('placeholder','點擊選擇時間');
          var iconRili = this.creatElement('i');
          iconRili.className = 'iconfont icon-rili';
    
          //拼接chooseDate
          chooseDate.appendChild(input);
          chooseDate.appendChild(iconRili);
          this.calendarMain.appendChild(chooseDate);
    
          //input綁定點擊事件
          this.queryElement('.chooseDate>input',true).addEventListener('click',function(){
              if( !cthis.calendarFound && !cthis.display ){                       //日歷未創建未顯示
                  cthis.create();                                                 //創建日歷并顯示
                  cthis.calendarFound = true;
                  cthis.display = true;
              }else if(cthis.display){                                           //日歷已創建已顯示
                  cthis.hide( cthis.queryElement('.calendar',true) );            //隱藏日歷
                  cthis.display = false;                                         //修改初始值為未顯示
              }else if(!cthis.display){                                          //日歷已創建未顯示
                  cthis.show( cthis.queryElement('.calendar',true) );             //顯示日歷
                  cthis.display = true;                                           //修改初始值為已顯示
              }
          })
      }
    
    5.當第一次點擊input框之后,創建并顯示日歷主體;其他非第一次點擊,只執行隱藏和顯示日歷主體
      //創建并繪制日歷
      create(){
          this.calendarFound = true;                   //修改初始值為已創建
          this.display = true;                         //修改初始值為已顯示
    
          //繪制calecdar
          var calendar = this.creatElement('div');
          calendar.className = 'calendar clear';
          var calendarShow = this.creatElement('div');
          calendarShow.className = 'c-show';                   //日歷左邊c-show
          var calendarBox = this.creatElement('div');
          calendarBox.className = 'c-box';                     //日歷右邊c-box
    
          //拼接calendar
          calendar.appendChild(calendarShow);
          calendar.appendChild(calendarBox);
          this.calendarMain.appendChild(calendar);
    
          //獲取當前時間保存在this.calendarDate對象里
          var weeks = ['Sun','Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'];
          var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
          this.calendarDate.year = this.date.getFullYear();        //年
          this.calendarDate.month = months[this.date.getMonth()];  //月
          this.calendarDate.day = this.date.getDate();             //日
          this.calendarDate.week = weeks[this.date.getDay()];      //周幾
    
          //獲取當月天數并展示主體
          this.calendarDate.Alldays = this.days(this.calendarDate.year,this.calendarDate.month);
          var calendarDate = this.calendarDate;
          this.initialCalendarShow(calendarDate,calendarShow,calendarBox);
          this.initialCalendarBox(calendarDate,calendarBox,calendarShow);
      }
    
    6.獲取當月天數
        days(year,month){
          var days = 30;
          switch (month){
              case 'Jan':
              case 'Mar':
              case 'May':
              case 'Jul':
              case 'Aug':
              case 'Oct':
              case 'Dec':
                  days = 31;
                  break;
              case 'Feb':
                  if( year % 4 === 0 && year % 100 !== 0 ){
                      days = 29;
                  }else if( year % 400 === 0 ){
                      days = 29;
                  }else{
                      days = 28;
                  }
                  break;
          }
          return days;
      }
    
    7.初始化主體部分的左右兩側
    //初始化calendarShow
     initialCalendarShow(date,calendarShow,calendarBox){
         var cthis = this;
    
         //繪制calendarShow
         calendarShow.innerHTML = '';
    
         var showYear = this.creatElement('a');
         showYear.className = 'year';
         showYear.innerHTML = date.year;
         var showWeek = this.creatElement('a');
         showWeek.className = 'week';
         showWeek.innerHTML = date.week + ',';
         var showDay = this.creatElement('a');
         showDay.className = 'day';
         showDay.innerHTML = date.month  + ' ' +  date.day;
    
         //拼接calendarShow
         calendarShow.appendChild(showYear);
         calendarShow.appendChild(showWeek);
         calendarShow.appendChild(showDay);
    
         //選年選月按鈕切換
         showYear.addEventListener('click',function(){
             cthis.hide(showWeek);                    //隱藏顯示的日期 
             cthis.hide(showDay);
             // 點擊年份之后,顯示'Choose Month'和年份列表
             if( showYear.innerHTML !== 'Choose Month' ){
                 showYear.innerHTML = 'Choose Month';
                 if( cthis.queryElement('.monthList',true) ){
                     cthis.hide( cthis.queryElement('.monthList',true) );
                 }
                 cthis.chooseYear(date,calendarShow,calendarBox);
             }else{
                 // 再次點擊時,切換為顯示'Choose Year',并顯示月份列表
                 showYear.innerHTML = 'Choose Year';
                 if( cthis.queryElement('.yearList',true) ){
                     cthis.hide( cthis.queryElement('.yearList',true) );
                 }
                 cthis.chooseMonth(date,calendarShow,calendarBox);
             }
         })
     }
    
    8.渲染年份列表
    //yearList
    chooseYear(date,calendarShow,calendarBox){
        var cthis = this;
        date.year = parseInt(date.year);
        if( !this.queryElement('.yearList',true) ){
            var yearList = cthis.creatElement('div');
            yearList.className = 'yearList';
            // 每頁顯示9個年份item
            // 當前年份的上一年顯示成上剪頭
            // 當前年份+9年顯示成下箭頭
            // 其他年份渲染成a標簽顯示
            for(var i = date.year - 1;i < date.year + 10;i++){
                if( i === date.year - 1 ){
                    var iconfontShang = cthis.creatElement('a');
                    iconfontShang.className = 'iconfont';
                    var iconShang = cthis.creatElement('i');
                    iconShang.className = 'iconfont icon-shang';
                    yearList.appendChild(iconfontShang);
                    iconfontShang.appendChild(iconShang);
                }else if( i === date.year + 9 ){
                    var iconfontXia = cthis.creatElement('a');
                    iconfontXia.className = 'iconfont';
                    var iconXia = cthis.creatElement('i');
                    iconXia.className = 'iconfont icon-xia';
                    yearList.appendChild(iconfontXia);
                    iconfontXia.appendChild(iconXia);
                }else{
                    var yearItem = cthis.creatElement('a');
                    yearItem.className = 'year-item';
                    yearItem.innerHTML = i;
                    yearList.appendChild(yearItem);
                    yearItem.addEventListener('click',function(){
                        date.year = this.innerHTML;
                        // 同時渲染右側title的年份
                        cthis.initialCalendarBox(date,calendarBox,calendarShow,'animate');
                    })
                }
            }
            // 拼接年份列表
            calendarShow.appendChild(yearList);
    
    
            //向下滾動
            // 將每一個當前顯示的年份+9,就是下一個年份列表
            iconfontXia.addEventListener('click',function(){
                var nextYearList = cthis.queryElement('.year-item');
                for(var i = 0;i < nextYearList.length;i++){
                    nextYearList[i].innerHTML = parseInt( nextYearList[i].innerHTML ) + 9;
                }
            })
    
            //向上滾動
            iconfontShang.addEventListener('click',function(){
                var lastYearList = cthis.queryElement('.year-item');
                for(var i = 0;i < lastYearList.length;i++){
                    lastYearList[i].innerHTML = parseInt(lastYearList[i].innerHTML) - 9;
                }
            })
    
        }else{
            this.show( this.queryElement('.yearList',true) );
        }
    }
    
    9.顯示月份列表
    //monthList
    chooseMonth(date,calendarShow,calendarBox){
        var cthis = this;
        if( !this.queryElement('.monthList',true) ){
            var monthList = cthis.creatElement('div');
            monthList.className = 'monthList';
            // 將月份渲染出來
            var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
            for(var i = 0;i < months.length;i++){
                var monthItem = cthis.creatElement('a');
                monthItem.className = 'month-item';
                monthItem.innerHTML = months[i];
                monthList.appendChild(monthItem)
            }
            calendarShow.appendChild(monthList);
        }else{
            this.show( this.queryElement('.monthList',true) );
        }
        // 給月份列表中每個月份添加點擊事件
        // 修改顯示日期的月份,渲染右側
        var monthItems = this.queryElement('.month-item');
        for(var i = 0;i<monthItems.length;i++){
            monthItems[i].addEventListener('click',function(){
                date.month = this.innerHTML;
                cthis.initialCalendarBox(date,calendarBox,calendarShow,'animate');
            })
        }
    }
    
    10.準備后日期數據后,最后渲染右側日歷主體的顯示日期的部分
    //初始化calendarBox
    initialCalendarBox(date,calendarBox,calendarShow,animate){
        var cthis = this;
    
        //繪制calendarBox
        calendarBox.innerHTML = '';
    
        var control = this.creatElement('div');
        control.className = 'c-year clear';
        var iconZuo = this.creatElement('i');
        iconZuo.className = 'iconfont icon-zuo';
        var currentYear = this.creatElement('a');
        currentYear.className = 'years';
        currentYear.innerHTML = date.month + ' ' + date.year;
        var iconGengDuo = this.creatElement('i');
        iconGengDuo.className = 'iconfont icon-gengduo';
    
        //拼接calendarBox
        calendarBox.appendChild(control);
        control.appendChild(iconZuo);
        control.appendChild(currentYear);
        control.appendChild(iconGengDuo);
    
        //顯示日歷數字部分
        this.dateTable(calendarBox,date,calendarShow,animate);
    
        //繪制控制按鈕
        var controlButton = this.creatElement('div');
        controlButton.className = 'c-button';
        var deterMine = this.creatElement('a');
        deterMine.className = 'go-determine';
        deterMine.innerHTML = 'OK';
        var today = this.creatElement('a');
        today.className = 'go-today';
        today.innerHTML = 'Today';
    
        //拼接控制按鈕
        calendarBox.appendChild(controlButton);
        controlButton.appendChild(deterMine);
        controlButton.appendChild(today);
    
        //today綁定點擊事件
        today.addEventListener('click',function(){
            var now = new Date();
            var weeks = ['Sun','Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'];
            var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
            date.year = now.getFullYear();
            date.month = months[now.getMonth()];
            date.day = now.getDate();
            date.week = weeks[now.getDay()];
            date.Alldays = cthis.days(date.year,date.month);
            cthis.initialCalendarBox(date,calendarBox,calendarShow,'animate');
            cthis.initialCalendarShow(date,calendarShow,calendarBox);
        })
    
        //ok綁定點擊事件
        deterMine.addEventListener('click',function(){
            cthis.hide( cthis.queryElement('.calendar',true) );
            cthis.display = false;
            cthis.queryElement('.chooseDate>input',true).value = date.day + ' ' + date.month + ' ' + date.year;
            cthis.initialCalendarShow(date,calendarShow,calendarBox);
        })
    
        //向左滾動
        this.queryElement('.icon-zuo',true).addEventListener('click',function(){
            var weeks = ['Sun','Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'];
            var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
            // 2-12月,僅月份減1再重新獲取當月天數即可
            if( date.month !== 'Jan' ){
                var currentIdx = months.indexOf(date.month);
                date.month = months[currentIdx-1];
                date.Alldays = cthis.days(date.year,date.month);
            }else{
                // 如果當前已經是1月,則年份減1 ,月份顯示為12月
                date.year -= 1;
                date.month = months[11];
            }
            date.day = 1;
            var currentDate = new Date(date.year + '/' + date.month + '/' + date.day);
            date.week = weeks[currentDate.getDay()];
            cthis.initialCalendarBox(date,calendarBox,calendarShow);
            cthis.initialCalendarShow(date,calendarShow,calendarBox);
        })
    
        //向右滾動
        this.queryElement('.icon-gengduo',true).addEventListener('click',function(){
            var weeks = ['Sun','Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'];
            var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'];
            // 1-11月,月份加1并獲取天數即可
            if( date.month !== 'Dec' ){
                var currentIdx = months.indexOf(date.month);
                date.month = months[currentIdx+1];
                date.Alldays = cthis.days(date.year,date.month);
            }else{
                // 12時,再下一個月就是下一年了,因此年份加1,顯示一月
                date.year += 1;
                date.month = months[0];
            }
            date.day = 1
            var currentDate = new Date(date.year + '/' + date.month + '/' + date.day);
            date.week = weeks[currentDate.getDay()];
            cthis.initialCalendarBox(date,calendarBox,calendarShow);
            cthis.initialCalendarShow(date,calendarShow,calendarBox);
        })
    }
    
    11.繪制日歷顯示日期部分時候用了table標簽
    //顯示日歷數字部分
    dateTable(calendarBox,date,calendarShow,animate){
       var cthis = this;
    
       //繪制date-box
       var table = this.creatElement('table');
       table.className = 'date-box';
       var thead = this.creatElement('thead');
       var theadTr = this.creatElement('tr');
       var header = ['S','M','T','W','T','F','S'];
       header.forEach(function(val){
           var th = cthis.creatElement('th');
           th.innerHTML = val;
           theadTr.appendChild(th);
       })
    
       //拼接date-box
       table.appendChild(thead);
       thead.appendChild(theadTr);
       table.className == 'animate' ? '' : 'animate';
    
       //得到每月第一天周幾
       var tbody = this.creatElement('tbody');
       var firstDay = new Date(date.year + '/' + date.month + '/' + 1).getDay();
    
       //計算出一個月中的每個周日
       //i表示本月table第幾天,date.Alldays+firstDay-1表示循環次數,需要加上table中空缺的前幾天
       for(var i = 0;i < date.Alldays + firstDay;i++){
           // 當是table的第1、7、14、21、28天,也就是table周日的位置,新建一行
           if( i === 0 || i % 7 === 0 ){            
               var tbodyTr = this.creatElement('tr');
           }
           // 非周日的,新建td
           var td = this.creatElement('td');
           // 只有當i循環到等于本月第一天的時候,才開始真正的本月table渲染
           if( i >= firstDay ){                         
               var a = this.creatElement('a');
               var currentDay = i - firstDay + 1;       //當前是本月幾號
               a.innerHTML = currentDay;
               // 當循環的到當前時間剛好是獲取的今天時
               if(  date.day === currentDay){
                   a.className = 'active';
               }
               a.addEventListener('click',function(){
                   var weeks = ['Sun','Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'];
                   date.day = parseInt(this.innerHTML);
                   var currentDate = new Date(date.year + '/' + date.month + '/' + date.day);
                   date.week = weeks[currentDate.getDay()];
                   cthis.initialCalendarBox(date,calendarBox,calendarShow);
                   cthis.initialCalendarShow(date,calendarShow,calendarBox);
               })
               td.appendChild(a);
           }
           tbodyTr.appendChild(td)
           if( i === 0 || i % 7 === 0 ){
               tbody.appendChild(tbodyTr);
           }
       }
       table.appendChild(tbody);
       calendarBox.appendChild(table);
    }
    
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容