JS日曆控制元件優化(增加時分秒)

龍恩0707發表於2014-12-21

JS日曆控制元件優化

     在今年7月份時候 寫了一篇關於 "JS日曆控制元件" 的文章 , 當時只支援 年月日 的日曆控制元件,現在優化如下:
     1. 在原基礎上 支援 yyyy-mm-dd 的年月日的控制元件。
     2. 在原基礎上增加支援 yyyy-mm-dd HH:MM 年月日時分的控制元件。
     3. 在原基礎上增加支援 yyyy-mm-dd HH:MM:SS 年月日時分秒的控制元件。
     4. 增加確定按鈕 及 今天 和關閉按鈕。當我切換到其他年份的時候,我點選 "今天"按鈕 就可以返回當前的年月份。
 
配置項如下:
 
  targetCls
  渲染日曆的class 預設為空
  beginyear   日曆的開始年份 預設為1949
  endyear   日曆的結束年份 預設為2049
  date   new Date() 預設日期 也可以在input框自己自定義值
  type

  日期格式。目前支援三種格式:

     1. "yyyy-mm-dd" 年月日

     2. "yyyy-mm-dd HH:MM:SS" 年月日時分秒。

    3. "yyyy-mm-dd HH:MM" 年月日時分
  separator 日期的分隔符 預設為 -
  wday  0, 設定周的第一天,預設從第一天開始
  language

物件:

{

      year: "年",

      month: "月",

      monthList:       ["1","2","3","4","5","6","7","8","9","10","11","12"],

      weekList: ["日","一","二","三","四","五","六"]}

比如如下demo年月日的示意圖如下:

時分秒的demo如下:

時分的demo如下:

HTML程式碼如下:

<p>
  開始時間:
   <input name="mydate" type="text" class="input_cxcalendar"  style="width:200px;">
  結束時間:
  <input name="mydate2" type="text" class="input_cxcalendar" style="width:200px;" >
</p>

當然在頭部要引入如下JS檔案:

<script src="jquery-1.9.1.js"></script>

<script src="calendar.js"></script>

依賴於jquery框架。後面的是日曆控制元件JS

初始化方式如下:

<script>
$('.input_cxcalendar').each(function(){
   var a = new Calendar({
       targetCls: $(this),
       type: 'yyyy-mm-dd' 或者 ‘yyyy-mm-dd HH:MM:SS’ 或者 ‘yyyy-mm-dd HH:MM’;
   },function(val){
       console.log(val);  // 回撥函式 當前選中的值
   });
});
</script> 

程式碼分析:

   1. 頁面初始化時,呼叫init()方法,生成日曆控制元件程式碼,如下所示:

       

    2. 初始化完成後,呼叫渲染日曆皮膚的函式 _renderCalendarPanel(),如下所示:\

        

   3. 在_renderCalendarPanel()函式做一些判斷如下:

      

     4.  在第三步定義了每月的天數 self.month_day; 定義了週末(週六,週日)的位置是第幾個,如下所示:

          

   接著程式碼如下:

 

如下設定wday = 2

_dayNumOfMonth函式的意思如下:

5. 下面我們接著來看看 通過年 月份來渲染天數 _renderBody()函式,如下:

    

如下所示

接著:

等。

JS所有程式碼如下:

/**
 * javascript日曆控制元件
 * @author tugenhua
 */

 function Calendar(options,callback){
    var self = this;
    self.options = $.extend({},defaults,options || {});
    self.targetCls = $(self.options.targetCls);
    if(self.targetCls.length < 1) {return;}
    self.language = self.options.language;

    // 顯示日曆皮膚
    self.flag = false;

    self.callback = callback;

    self._init();
    self._bindEnv();
    
 };
 $.extend(Calendar.prototype,{
    
    _init: function(){
        var self = this;
        // 先渲染日曆皮膚
        self._renderCalendarPanel();
    },
    _renderCalendarPanel: function(){
        var self = this,
            options = self.options;
        // 如果input輸入框有日期的話 
        if(self.targetCls.val().length > 0) {
            self.date = self._dateParse(self.targetCls.val());
        }
        self.date = new Date(self.date);
        if(isNaN(self.date.getFullYear())){
            self.date = new Date();
        }
        var defYear = self.date.getFullYear(),
            defMonth = self.date.getMonth() + 1;

        // 定義每月的天數
        self.month_day = new Array(31,28+self._leapYear(defYear),31,30,31,30,31,31,30,31,30,31);

        // 定義每週的日期
        self.date_name_week = self.language.weekList;

        // 定義週末
        var saturday = 6 - options.wdays,
            sunday = (7-options.wdays >= 7) ? 0 : (7-options.wday);
        
        // 建立日曆皮膚dom節點
        var date_pane = $('<div class="cxcalendar"></div>'),
            date_hd = $('<div class="date_hd"></div>').appendTo(date_pane),
            date_table = $('<table class="date_table"></table>').appendTo(date_pane);
        
        // type 為 yyyy-mm-dd 時候 就不顯示時間
        if(options.type == 'yyyy-mm-dd HH:MM' || options.type == 'yyyy-mm-dd HH:MM:SS') {
            var    date_time = $('<div class="date_time"><span class="spinner"><input type="text" class="spinner-text"><i class="spinner-arrow spinner-arrow-up"></i><i class="spinner-arrow spinner-arrow-down"></i></span></div>')
            .appendTo(date_pane);
        }
        var    date_button = $('<div class="date_button"><a href="javascript:void(0)" class="date-current">今天</a><a href="javascript:void(0)" class="date-closed">關閉</a><a href="javascript:void(0)" class="date-ok">確定</a></div>').appendTo(date_pane);
        date_hd.html("<a class='date_pre' href='javascript://' rel='prev'>&lt;</a><a class='date_next' href='javascript://' rel='next'>&gt;</a>");
        
        var date_txt = $('<div class="date_txt"></div>').appendTo(date_hd),
            date_set = $('<div class="date_set"></div>').appendTo(date_hd),
            html = "";

        for(var i = options.beginyear; i < options.endyear; i++) {
            html +="<option value='"+i+"'>"+i+"</option>";
        }
        var year_list = $('<select class="year_set"></select>').html(html).appendTo(date_set).val(defYear);
        
        date_set.append(" - ");
        html = '';
        for(var i = 0; i < 12; i++) {
            html += '<option value="'+(i+1)+'">'+self.language.monthList[i]+'</option>';
        }
        var month_list = $('<select class="month_set"></select>').html(html).appendTo(date_set).val(defMonth);
        html = '<thead><tr>';
        // 遍歷一週7天
        for(var i = 0; i < 7; i++) {
            html += "<th class='"
            // 週末高亮
            if(i == saturday) {
                html+= " sat";
            }else if(i == sunday) {
                html+= " sun";
            };
            html+="'>";
            html+= (i+options.wday * 1 < 7) ? self.date_name_week[i+options.wday] : self.date_name_week[i+options.wday - 7];
            html+="</th>";
        };
        html +="</tr></thead>";
        html +="<tbody></tbody>";
        date_table.html(html);

        // 皮膚及背景遮擋層插入到頁面中
        date_pane.appendTo("body");

        // 建立遮罩層的目地是:只顯示一個日曆皮膚
        var block_bg=$("<div class='cxcalendar_lock'></div>").appendTo("body");
        
        // 賦值 全域性
        self.dateTxt = date_txt;
        self.yearList = year_list;
        self.monthList = month_list;
        self.dateTable = date_table;
        self.saturday = saturday;
        self.sunday = sunday;
        self.datePane = date_pane;
        self.blockBg = block_bg;
        self.dateSet = date_set;
        self.defYear = defYear;
        self.defMonth = defMonth;
        self.dateTime = date_time;

        //  根據當前的時間 渲染時分秒
        self._renderTime(self.date);

        // 根據年份 月份來渲染天
        self._renderBody(defYear,defMonth);
    },
    // 返回 年月分的 天數
    _dayNumOfMonth: function(Year,Month){
        var d = new Date(Year,Month,0);
        return d.getDate();
    },
    /*
     * 根據當前的時間,渲染時分秒
     */
    _renderTime: function(date){
        var self = this,
            options = self.options;
        var hours = new Date(date).getHours(),
            minutes = new Date(date).getMinutes(),
            seconds = new Date(date).getSeconds(),
            curTime;
        if(hours < 10) {
            hours = '0' + hours;
        }
        if(minutes < 10) {
            minutes = '0' + minutes;
        }
        if(seconds < 10) {
            seconds = '0' + seconds;
        }
        if(options.type == 'yyyy-mm-dd HH:MM:SS') {
            curTime = hours + ":" + minutes + ":" + seconds;

        }else if(options.type == 'yyyy-mm-dd HH:MM') {
            curTime = hours + ":" + minutes;

        }
        
        $(self.dateTime).find('.spinner-text').val(curTime);
    },
    /*
     * 渲染日曆天數
     * @param {y,m} 年 月
     */
    _renderBody: function(y,m){
        var self = this;
        var options = self.options;
        if(m < 1) {
            y--;
            m = 12;
        }else if(m > 12) {
            y++;
            m = 1;
        }
        var tempM = m,
            cur_m = m;

        m--;  // 月份從0開始的
        var prevMonth = tempM - 1,  //上個月的月份
            prevDay = self._dayNumOfMonth(y,tempM - 1), // 上個月的天數
            nextMonth = tempM + 1,   // 下個月的月份
            nextDay = self._dayNumOfMonth(y,tempM + 1),  //下個月的天數
            curDay = self._dayNumOfMonth(y,tempM);       // 當前月份的天數

        self.month_day[1]=28+self._leapYear(y);  //閏年的話 29天 否則 28天
        var temp_html = "",
            temp_date = new Date(y,m,1);
        var now_date = new Date();
        now_date.setHours(0);
        now_date.setMinutes(0);
        now_date.setSeconds(0);
        
        // 如果輸入框有值的話
        if(self.targetCls.val().length > 0) {
            var val_date=self._dateParse(self.targetCls.val())
        }
        val_date=new Date(val_date);
        if(isNaN(val_date.getFullYear())){
            val_date=null;
        };
        // 獲取當月的第一天
        var firstDay = temp_date.getDay() - options.wday < 0 ? 
            temp_date.getDay() - options.wday + 7 : 
            temp_date.getDay() - options.wday;
        

        // 每月所需要的行數
        var monthRows = Math.ceil((firstDay+self.month_day[m]) / 7);
        var td_num,
            day_num,
            diff_now,
            diff_set;
        var disabled;
        for(var i= 0; i < monthRows; i++) {
            temp_html += "<tr>";
            for(var j = 0; j < 7; j++) {
                td_num=i*7+j;
                day_num=td_num-firstDay+1;
                if(day_num<=0) {
                    if(day_num == 0) {
                        day_num = prevDay - day_num
                        text_m = prevMonth
                    }else {
                        day_num = prevDay + day_num;
                        text_m = prevMonth
                    }
                    
                }else if(day_num > self.month_day[m]){
                    day_num = day_num - curDay;
                    text_m = nextMonth
                }else {
                    text_m     = cur_m;
                }
                temp_html+="<td";
                if(typeof(day_num) == 'number') {
                    diff_now=null;
                    diff_set=null;
                    temp_date = new Date(y,m,day_num);

                    if(text_m == cur_m) {
                        diff_now=Date.parse(now_date)-Date.parse(temp_date);
                        diff_set=Date.parse(val_date)-Date.parse(temp_date);
                    }
                    if(cur_m > text_m || cur_m < text_m) {
                        disabled = 'disabled';
                    }else {
                        disabled = "";
                    }
                    temp_html+=(" title='"+y+options.separator+tempM+options.separator+day_num+"' class='num "+disabled+"");

                    // 高亮週末、今天、選中
                    if(diff_set==0){    //選中的時候 增加select 類名
                        temp_html+=" selected";
                    }else if(diff_now==0){
                        temp_html+=" now";   // 當前時間增加now類名
                    }else if(j==self.saturday){
                        temp_html+=" sat";   // 週六增加sat類名
                    }else if(j==self.sunday){
                        temp_html+=" sun";   // 週日增加sun類名
                    };
                    temp_html+=("'");
                };
                temp_html+=(" date-day='"+day_num+"'>"+day_num+"</td>");
            }
            temp_html+="</tr>";
        }
     
        $(self.dateTable).find("tbody").html(temp_html);
        $(self.dateTxt).html("<span class='y'>"+y+"</span>"+options.language.year+"<span class='m'>"+options.language.monthList[m]+"</span>"+options.language.month);
        $(self.yearList).val(y);
        $(self.monthList).val(m+1);
        
        return this;
    },
    _dateParse: function(date){
        var newdate = date;
        newdate=newdate.replace(/\./g,"/");
        newdate=newdate.replace(/-/g,"/");
        newdate=newdate.replace(/\//g,"/");
        newdate=Date.parse(newdate);
        return newdate;
    },
    /*
     * 判斷是否是閏年
     * @param y 年份
     * 1.能被4整除且不能被100整除 2.能被100整除且能被400整除
     */
    _leapYear: function(y) {
        return ((y%4==0 && y%100!=0) || y%400==0) ? 1 : 0;
    },
    _bindEnv: function(){
        var self = this;
        var options = self.options;
        $(self.targetCls).unbind('click').bind('click',function(){
            self.show();

            // 渲染時分秒
            self._renderTime(new Date());
        });

        // 關閉皮膚事件
        self.blockBg.unbind('click').bind("click",function(){
            self.hide();
        });

        // 點選上一頁 下一頁事件
        self.datePane.delegate('a','click',function(){
            if(!this.rel){return};
            var _rel = this.rel;
            if(_rel == 'prev') {
                self._renderBody(self.yearList.val(),parseInt(self.monthList.val(),10) -1);    
                return;
            }else if(_rel == 'next') {
                self._renderBody(self.yearList.val(),parseInt(self.monthList.val(),10) +1);
                return;
            }
        });

        // 選擇日期事件
        self.datePane.delegate('td','click',function(){
            var _this = $(this);
            if(_this.hasClass('num') && !_this.hasClass('disabled')) {
                self.dateTable.find("td").removeClass("selected");
                _this.addClass("selected");
                var day = _this.attr("date-day");
                if(options.type == 'yyyy-mm-dd') {
                    self._selectDay(day);
                }
                
            }
        });
        
        // 顯示年月選擇
        self.dateTxt.unbind('click').bind("click",function(){
            self.dateTxt.hide();
            self.dateSet.show();
        });

        //更改年月事件
        self.yearList.unbind('change').bind("change",function(){
            self._renderBody(self.yearList.val(),self.monthList.val());
        });
        self.monthList.unbind('change').bind("change",function(){
            self._renderBody(self.yearList.val(),self.monthList.val());
        });

        // 點選確定按鈕
        self.datePane.delegate('.date-ok','click',function(){
            var nums = $(self.dateTable).find('.num'),
                flag = false;
            for(var i = 0, ilen = nums.length; i < ilen; i++) {
                if($(nums[i]).hasClass("selected")) {
                    var day = $(nums[i]).attr("date-day");
                    flag = true;
                    break;
                }else {
                    if($(nums[i]).hasClass("now")) {
                        var day = $(nums[i]).attr("date-day");
                        flag = true;
                        continue;
                    }
                }
            }
            if(flag) {
                self._selectDay(day);
            }    
        });
        // 點選關閉按鈕
        self.datePane.delegate('.date-closed','click',function(){
            self.hide();
        });

        // 點選今天按鈕
        self.datePane.delegate('.date-current','click',function(){
            // 獲取當前第幾號
            var date = new Date(),
                curDay = date.getDate(),
                curYear = date.getFullYear(),
                curMonth = date.getMonth() + 1;
            self._renderBody(curYear,curMonth);
            
            self.dateTable.find("td.num").removeClass("selected");
            self.dateTable.find("td.num").each(function(){
                var day = $(this).attr("date-day");
                if(day == curDay) {
                    $(this).addClass("selected");
                }
            });
        });

        // 點選箭頭向上按鈕 
        self.datePane.delegate('.spinner-arrow-up','click',function(){
            self._inputValChange(true);
        });

        // 點選箭頭向下按鈕 
        self.datePane.delegate('.spinner-arrow-down','click',function(){
            self._inputValChange(false);
        });
    },
    /* 
     * 選擇某一天的時候 把值存入輸入框裡 且皮膚隱藏
     * @_selectDay {private}
     */
    _selectDay: function(d) {
        var self = this;
        var year,
            month;
        month = self.monthList.val();
        day = d;
        var options = self.options;
        month="0" + self.monthList.val();
        day= "0" + d;
        month=month.substr((month.length-2),month.length);
        day=day.substr((day.length-2),day.length);

        if(options.type == 'yyyy-mm-dd') {
            self.targetCls.val(self.yearList.val()+options.separator+month+options.separator+day);
            self.hide();
            self.callback && $.isFunction(self.callback) && self.callback(self.yearList.val()+options.separator+month+options.separator+day);
        }else if(options.type == 'yyyy-mm-dd HH:MM:SS' || options.type == 'yyyy-mm-dd HH:MM'){
            self.targetCls.val(self.yearList.val()+options.separator+month+options.separator+day+ " " +$(self.dateTime).find('.spinner-text').val());
            self.hide();
            self.callback && $.isFunction(self.callback) && self.callback(self.yearList.val()+options.separator+month+options.separator+day+ " " +$(self.dateTime).find('.spinner-text').val());
        }
        return this;
    },
    /*
     * 點選向上或者向下 時間輸入框值改變
     * @param {flag} boolean 布林型。如果為true 則是往上加 否則是往下減
     */
    _inputValChange: function(flag){
        var self = this,
            options = self.options;

        var inputVal = $(self.datePane).find('.spinner-text').val();
        var arrs = inputVal.split(":"),
            hour = arrs[0] ? arrs[0] : '',
            minute = arrs[1] ? arrs[1] : '',
            second = arrs[2] ? arrs[2] : '';
        if(flag) {
            hour++;
            if(hour >= 24) {
                return;
            }else if(hour < 10) {
                hour = "0" + hour;
            }
        }else {
            hour--;
            if(hour < 0) {
                return;
            }else if(hour < 10) {
                hour = '0' + hour;
            }
        }
        var val;
        if(options.type == "yyyy-mm-dd HH:MM:SS") {
            val = hour + ":" + minute + ":" + second;

        }else if(options.type == 'yyyy-mm-dd HH:MM') {
            val = hour + ":" + minute;

        }
        $('.spinner-text',self.datePane).val(val);
        
    },
    /*
     * 顯示日曆皮膚
     * @method  show {public}
     */
    show: function(){
        var self = this;
        if(self.flag) {
            return;
        }
        var doc_w = document.body.clientWidth,
            doc_h = document.body.clientHeight,
            pane_top = self.targetCls.offset().top,
            pane_left = self.targetCls.offset().left,
            obj_w = self.targetCls.outerWidth(),
            obj_h = self.targetCls.outerHeight();
        pane_top= pane_top+obj_h;
        self.datePane.css({"top":pane_top,"left":pane_left}).show();
        self.blockBg.css({width:doc_w,height:doc_h}).show();
        self.flag = true;
        return this;
    },
    /*
     * 清除日期
     * @method clear {public}
     */
    clear: function(){
        var self = this;
        self.targetCls.val('');
        self._renderBody(self.defYear,self.defMonth);
        self.hide();
        return this;
    },
    /*
     * 獲取當前選中的日期
     * @method getValue {public}
     * @return val
     */
     getValue: function(){
        var self = this;
        return self.targetCls.val();
     },
    /*
     * 隱藏日曆皮膚
     */
    hide: function(){
        var self = this;
        if(!self.flag) {return;}
        self.datePane.hide();
        self.blockBg.hide();
        self.dateSet.hide();
        self.dateTxt.show();
        self.flag = false;
        return this;
    }
 });

 var defaults = {
    targetCls        :        '',             //渲染日曆的class
    beginyear        :        1949,           //開始年份
    endyear          :        2050,           //結束年份
    date             :        new Date(),     // 預設日期
    type             :        "yyyy-mm-dd",      // 日期格式
    separator        :        "-",              // 日期連結符
    wday             :        0,              // 周第一天
    language         :       {
                                year:"年",
                                month:"月",
                                monthList:["1","2","3","4","5","6","7","8","9","10","11","12"],
                                weekList:["日","一","二","三","四","五","六"]}        
 };
View Code

JS日曆控制元件demo下載

相關文章