百度前端技術學院上有一個任務,要實現一個日期選擇組件,本文由此而來~
- 看看需求
- 組件默認一直呈顯示狀態
- 通過某種方式選擇年、月,選擇了年月后,日期列表做相應切換
- 通過單擊某個具體的日期進行日期選擇
- 組件初始化時,可配置可選日期的上下限。可選日期和不可選日期需要有樣式上的區別
- 提供設定日期的接口,指定具體日期,日歷面板相應日期選中
- 日期選擇面板默認隱藏,會顯示一個日期顯示框和一個按鈕,點擊這兩個部分,會浮出日歷面板。再點擊則隱藏。
- 點擊選擇具體日期后,面板隱藏,日期顯示框中顯示選取的日期
- 增加一個接口,用於當用戶選擇日期后的回調處理
- 增加一個參數及相應接口方法,來決定這個日歷組件是選擇具體某天日期,還是選擇一個時間段
- 當設置為選擇時間段時,需要在日歷面板上點擊兩個日期來完成一次選擇,兩個日期中,較早的為起始時間,較晚的為結束時間,選擇的時間段用特殊樣式標示
- 增加參數及響應接口方法,允許設置時間段選擇的最小或最大跨度,並提供當不滿足跨度設置時的默認處理及回調函數接口
- 在彈出的日期段選擇面板中增加確認和取消按鈕
先完成一個組件的基本結構
(function(window,document){
function Calendar(options){
//傳入配置的中的參數
this.init();
}
Calendar.prototype={
init:function(){
this.createDom();
this.loadCss();
this.cacheDom();
this.bindEvents();
this.render();
},
loadCss:function(){
// 把組件所需的樣式表動態加載進來
},
createDom:function(){
// 創建dom對象或者創建html片段或者創建template
},
cacheDom:function(){
// 存儲dom 對象
},
bindEvents:function(){
//事件綁定
},
render:function(){
//渲染函數,更新數據或樣式
}
}
window.Calendar=Calendar;//把組件對象綁定到全局
}(window,document));
通常我寫組件時的基本結構如上,你可以根據組件的需要或者自己習慣進行編寫。然后就可以在html里面添加以下的代碼就可以調用我們的組件了,
<script src='calendar.js></script>
<script type='text/javascript'>
var a=new Calendar({
// 各種配置
/* 類似於 id:'myCalendar'
onSelected:function(){
alert('hello');
}
*/
});
</script>
下面再看一下我們的需求,我們來一 一分析
需求也不是很多嘛,手動斜眼~
先上圖,根據圖再慢慢分析
其實我們看了需求之后,每個人都會有一個大概的思路,下面說一下我的思路
首先,要實現一個日期選擇器,最重要的就是要有一個日歷,根據不同的年份和月份,日期面板上回顯示每一天和對應的周幾~
其實實現這一點的話就兩點
- 第一,要根據年份和月份算出每月有多少天
- 第二,要計算出每月的第一天(1號)是周幾
偽代碼如下:
/**
* @param {string} year 年份
* @param {string} month 月份
* @param {string} day 號
* @return {object} message
* message{
* year 年份
* month 月份
* monthLen 那個月的天數
* 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;
},
我想看完代碼之后大家應該比較疑惑的是獲取每個月天數的那句代碼,這個比較優雅的做法是從這里看到的,
注意:在Date對象里month為0代表的是1月份,month為5代表6月份,所以我new Date(year,5,0)代表的六月份的第0天,即5月份的最后一天,所以還可以用getDate()獲取5月份的長度,getDate方法是返回指定日期對象的月份中的第幾天(1-31)。
所以當我們點擊了月份加減/年份加減的按鈕時,向calculate函數傳入變化后的year,month參數,然后進行渲染,日歷面板改變
其次,”選擇時間段並且另處於開始時間和結束時間之間的日期添加特殊的樣式“這一點也是花了不少時間來寫,
偽代碼如下:
// 初始化
var firstDate,secondDate=[0,0,0];
//點擊日歷面板上的日期的點擊事件的執行函數的片段,每當點擊事件被觸發,就會執行該片段
if(self.isSelectRange){
var date=[self.year.innerHTML,self.month.innerHTML,ele.innerHTML];
if(self.firstDate[0]===0){//
if(self.secondDate[0]===0){//兩個日期都沒有被設置
self.firstDate=date;
}else{//firstDate沒有被設置,secondDate已經被設置,
}
}else{
if(self.secondDate[0]===0){//firstDate已經設置,
self.secondDate=date;
if(compareDate(self.firstDate.join('/'),self.secondDate.join('/'))){//如果第一個選擇的日期大於第二次選擇的日期,進行交換
self.firstDate=[self.secondDate,self.secondDate=self.firstDate][0];
}
}else{//兩個日期都已經被設置,已經選擇了兩個元素,再次選擇則都
self.secondDate=[0,0,0];
self.firstDate=date;
self.clearDayInRangeStyle();
}
}
self.day.innerHTML=ele.innerHTML;
self.render();
firstDate,secondDate分別代表開始時間和結束時間。每次觸發日期的點擊事件時,就會執行以上的代碼片段,對firstDate和secondDate進行更改,這樣的話,無論是我對日歷面板進行更新或者對開始時間和結束時間之間的日期顯示不同的樣式,都可以通過firstDate和secondDate來實現。
顯示不同的樣式就判斷日期是否在開始時間和結束時間之間,每次重新render的時候就給選擇過的firstDate和secondDate添加樣式。
包括計算開始時間和結束時間之間的跨度是否在設定的跨度內,我們點擊按鈕后進行判斷。
最后,看看render函數怎么實現
關於render函數,有以下幾點需要注意:
- 清除日歷面板上的所有內容和樣式,樣式通過清除每個單元格上的類實現
- 根據每月1號是周幾和每月的長度生成每月的日歷
- 根據記錄的fisrtDate和secondDate來顯示已經選擇過的選擇的樣式
以上大概是我的思路,我也實現了一個組件,有興趣的朋友可以點這里,歡迎找bug~