美文网首页js
原生Js实现一个日历组件

原生Js实现一个日历组件

作者: 闪电西兰花 | 来源:发表于2018-09-19 21:56 被阅读660次
    • 日历组件图示如下:


      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);
      }
      

    相关文章

      网友评论

        本文标题:原生Js实现一个日历组件

        本文链接:https://www.haomeiwen.com/subject/wjhpnftx.html