怎樣實現(xiàn)一個datePicker(日期選擇)組件

百度前端技術(shù)學(xué)院上有一個任務(wù),要實現(xiàn)一個日期選擇組件,本文由此而來~

  1. 看看需求
  • 組件默認一直呈顯示狀態(tài)
  • 通過某種方式選擇年、月,選擇了年月后,日期列表做相應(yīng)切換
  • 通過單擊某個具體的日期進行日期選擇
  • 組件初始化時,可配置可選日期的上下限??蛇x日期和不可選日期需要有樣式上的區(qū)別
  • 提供設(shè)定日期的接口,指定具體日期,日歷面板相應(yīng)日期選中
  • 日期選擇面板默認隱藏,會顯示一個日期顯示框和一個按鈕,點擊這兩個部分,會浮出日歷面板。再點擊則隱藏。
  • 點擊選擇具體日期后,面板隱藏,日期顯示框中顯示選取的日期
  • 增加一個接口,用于當(dāng)用戶選擇日期后的回調(diào)處理
  • 增加一個參數(shù)及相應(yīng)接口方法,來決定這個日歷組件是選擇具體某天日期,還是選擇一個時間段
  • 當(dāng)設(shè)置為選擇時間段時,需要在日歷面板上點擊兩個日期來完成一次選擇,兩個日期中,較早的為起始時間,較晚的為結(jié)束時間,選擇的時間段用特殊樣式標(biāo)示
  • 增加參數(shù)及響應(yīng)接口方法,允許設(shè)置時間段選擇的最小或最大跨度,并提供當(dāng)不滿足跨度設(shè)置時的默認處理及回調(diào)函數(shù)接口
  • 在彈出的日期段選擇面板中增加確認和取消按鈕

先完成一個組件的基本結(jié)構(gòu)

    (function(window,document){
       function Calendar(options){
          //傳入配置的中的參數(shù)
          this.init();
       } 
       Calendar.prototype={
            init:function(){
               this.createDom();
               this.loadCss();
               this.cacheDom();
               this.bindEvents();
               this.render();
            },
            loadCss:function(){
               // 把組件所需的樣式表動態(tài)加載進來
            },
            createDom:function(){
               // 創(chuàng)建dom對象或者創(chuàng)建html片段或者創(chuàng)建template
            },
            cacheDom:function(){
               // 存儲dom 對象
            },
            bindEvents:function(){
               //事件綁定
            },
            render:function(){
              //渲染函數(shù),更新數(shù)據(jù)或樣式
            }
       }
       window.Calendar=Calendar;//把組件對象綁定到全局
    }(window,document));

通常我寫組件時的基本結(jié)構(gòu)如上,你可以根據(jù)組件的需要或者自己習(xí)慣進行編寫。然后就可以在html里面添加以下的代碼就可以調(diào)用我們的組件了,

<script src='calendar.js></script>
<script type='text/javascript'>
   var a=new Calendar({
      // 各種配置
      /* 類似于 id:'myCalendar'
         onSelected:function(){
                    alert('hello');
        }
     */
   });
</script>

下面再看一下我們的需求,我們來一 一分析

需求也不是很多嘛,手動斜眼~
先上圖,根據(jù)圖再慢慢分析

概要圖
概要圖

其實我們看了需求之后,每個人都會有一個大概的思路,下面說一下我的思路
首先,要實現(xiàn)一個日期選擇器,最重要的就是要有一個日歷,根據(jù)不同的年份和月份,日期面板上回顯示每一天和對應(yīng)的周幾~
其實實現(xiàn)這一點的話就兩點

  • 第一,要根據(jù)年份和月份算出每月有多少天
  • 第二,要計算出每月的第一天(1號)是周幾
    偽代碼如下:
 /**
     * @param  {string} year  年份
     * @param  {string} month 月份
     * @param  {string} day   號
     * @return {object}  message
     * message{
     * year   年份
     * month  月份
     * monthLen  那個月的天數(shù)
     * whichDay  1號是周幾
     * day       號
     * }    
     */
     function calculate(year,month,day){
                var date=year+'/'+month+'/'+'1';
                var whichDay=new Date(date).getDay();
              var message={
                    year:year,
                    month:month,
                    monthLen:new Date(year,month,0).getDate(),
                    whichDay:whichDay,
                    day:day
              };
              return message;
     },

我想看完代碼之后大家應(yīng)該比較疑惑的是獲取每個月天數(shù)的那句代碼,這個比較優(yōu)雅的做法是從這里看到的,
注意:在Date對象里month為0代表的是1月份,month為5代表6月份,所以我new Date(year,5,0)代表的六月份的第0天,即5月份的最后一天,所以還可以用getDate()獲取5月份的長度,getDate方法是返回指定日期對象的月份中的第幾天(1-31)。
所以當(dāng)我們點擊了月份加減/年份加減的按鈕時,向calculate函數(shù)傳入變化后的year,month參數(shù),然后進行渲染,日歷面板改變

其次,”選擇時間段并且另處于開始時間和結(jié)束時間之間的日期添加特殊的樣式“這一點也是花了不少時間來寫,
偽代碼如下:

// 初始化
var firstDate,secondDate=[0,0,0];
//點擊日歷面板上的日期的點擊事件的執(zhí)行函數(shù)的片段,每當(dāng)點擊事件被觸發(fā),就會執(zhí)行該片段

if(self.isSelectRange){
             var date=[self.year.innerHTML,self.month.innerHTML,ele.innerHTML];            
             if(self.firstDate[0]===0){// 
                if(self.secondDate[0]===0){//兩個日期都沒有被設(shè)置
                     self.firstDate=date;
                }else{//firstDate沒有被設(shè)置,secondDate已經(jīng)被設(shè)置,
                     
                }
             }else{
                if(self.secondDate[0]===0){//firstDate已經(jīng)設(shè)置,
                    self.secondDate=date;
                    if(compareDate(self.firstDate.join('/'),self.secondDate.join('/'))){//如果第一個選擇的日期大于第二次選擇的日期,進行交換
                        self.firstDate=[self.secondDate,self.secondDate=self.firstDate][0];
                    } 
                }else{//兩個日期都已經(jīng)被設(shè)置,已經(jīng)選擇了兩個元素,再次選擇則都
                   self.secondDate=[0,0,0];
                   self.firstDate=date;
                   self.clearDayInRangeStyle();
                }
             }
             self.day.innerHTML=ele.innerHTML;
             self.render();

firstDate,secondDate分別代表開始時間和結(jié)束時間。每次觸發(fā)日期的點擊事件時,就會執(zhí)行以上的代碼片段,對firstDate和secondDate進行更改,這樣的話,無論是我對日歷面板進行更新或者對開始時間和結(jié)束時間之間的日期顯示不同的樣式,都可以通過firstDate和secondDate來實現(xiàn)。

顯示不同的樣式就判斷日期是否在開始時間和結(jié)束時間之間,每次重新render的時候就給選擇過的firstDate和secondDate添加樣式。

包括計算開始時間和結(jié)束時間之間的跨度是否在設(shè)定的跨度內(nèi),我們點擊按鈕后進行判斷。
最后,看看render函數(shù)怎么實現(xiàn)
關(guān)于render函數(shù),有以下幾點需要注意:

  • 清除日歷面板上的所有內(nèi)容和樣式,樣式通過清除每個單元格上的類實現(xiàn)
  • 根據(jù)每月1號是周幾和每月的長度生成每月的日歷
  • 根據(jù)記錄的fisrtDate和secondDate來顯示已經(jīng)選擇過的選擇的樣式

以上大概是我的思路,我也實現(xiàn)了一個組件,有興趣的朋友可以點這里,歡迎找bug~
ps:文筆還是不行,文章寫的好爛。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,189評論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,169評論 22 665
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,429評論 4 61
  • 手機上的一個抓娃娃小游戲原型,有設(shè)定抓住的幾率,這里就不做判斷了,只實現(xiàn)抓取的效果! HTML JS CSS
    fixppy閱讀 1,248評論 0 2
  • 概覽與綜述 基本形式 解釋: DOCTYPE 聲明了文檔類型 位于標(biāo)簽 描述了文檔附加信息 位于標(biāo)簽 ...
    熊白白閱讀 388評論 0 2

友情鏈接更多精彩內(nèi)容