自己写的一款jQuery日期选择器插件

Published: 2015-05-30 Category: javascript Tags: javascript插件

##基于jQuery写的一款日期选择器插件##

兼容性

IE 6/7/8/9/10/11、Firefox、chrome、Safari、360、搜狗…

核心功能

  • 选择日期,翻月,翻年,选择年份区间。
  • 提供农历信息。
  • 特色功能:强容错、按键控制(支持up、down、left、right、Enter、Esc、pageUp、pageDown)、不限于绑定元素、自动定位弹框位置(自动判断上下左右距离选择合适位置弹框)、回调函数(自行处理返回的日期对象)
  • 代码特色:模板数据分离、模板引擎、DOM节点缓存、按需加载…

因为核心代码较多,就不大片大片的贴代码了。下面贴一些核心处理代码

/**
 * 日期选择类(依赖jquery)
 * @Author: 张佳(jia58960)
 * @Update: 2014/12/09
 * @param {Object} target 必选,需要绑定的元素对象,要求是jQuery对象
 * @param {Date} date 可选,初始化的时间
 * @param {String} dateFormat 可选,日期显示格式。如"yyyy-MM-dd"
 * @param {Function} callback 可选,选择日期后的回调
 * @param {String} direction 可选,日历选择器的展示位置
 * 
 * @examples
 * new CalendarSelector({container:$("#demo"),date:'2014-11-12',dateFormat:'yyyy-MM-dd',callback});
 * 
 * @returns {Object} dateObj 选中的日期对象
            {String} dateString 指定格式的日期字符串(2014-12-09)
            {Int} year 选中日期的年份 2014
            {Int} month 选中日期的月份 12
            {Int} date 选中日期的天 9
            {String} lunarDate 选中日期日的农历表示 十八
            {String} lunarMonth 选中日期月的农历表示 十月
            {String} lunarYear 选中日期的年份表示 二零一四
            {String} hsebYear 选中日期的干支年份表示 甲午年
 */

/**
 * 封装好代码中可能会经常用到的工具类
 * 提供一般的工具方法,抽离原因是希望日历选择器可以独立适用于各页面
 */
var Tools = {
        Date: {
            /**
             * 将日期字符串转换成标准日期对象
             * @param {String} str 时间字符串,如 2014/01/01 或者2014-1-1
             */
            parse: function(str) {
                if (/^\d{10}$/.test(str)) {
                    return new Date(str * 1000);
                } else if (/^\d{13}$/.test(str)) {
                    return new Date(str * 1);
                }
                str = Tools.Text.trim(str);
                var reg = /^(\d{4})[-\/](\d{1,2})[-\/](\d{1,2})$/;
                var m = str.match(reg);
                if (m) {
                    var year = m[1];
                    var month = parseInt(m[2] - 1, 10);
                    var day = parseInt(m[3], 10);
                    var hour = parseInt(m[4], 10);
                    return new Date(year, month, day);
                } else {
                    return undefined;
                }
            },
            /**
             * 将时间对象格式化成指定格式的时间字符串
             * @param {Date} date 时间对象
             * @param {String} text 要格式化的文本格式
             * @returns {String}
             * @example
             * Tools.Date.formatToString(new Date(), 'yyyy-MM-dd')
             */
            formatToString: function(date, text) {
                var reg = /yyyy|yy|M+|d+|S|w/g;
                text = text.replace(reg, function(pattern) {
                    var result;
                    switch (pattern) {
                        case "yyyy":
                            result = date.getFullYear();
                            break;
                        case "yy":
                            result = date.getFullYear().toString().substring(2);
                            break;
                        case "M":
                        case "MM":
                            result = date.getMonth() + 1;
                            break;
                        case "dd":
                        case "d":
                            result = date.getDate();
                            break;
                        default:
                            result = "";
                            break;
                    }
                    if (pattern.length == 2 && result.toString().length == 1) {
                        result = "0" + result;
                    }
                    return result;
                });
                return text;
            }
        },

        Text: {
            /**
             * 格式化字符串,提供数组和object两种方式(本质就是一个小的模板引擎)
             * @example
             * Tools.Text.format("hello,{name}",{name:"kitty"})
             * Tools.Text.format("hello,{0}",["kitty"])
             * @returns {String}
             */
            format: function(str, arr) {
                var reg;
                if ($.isArray(arr)) {
                    reg = /\{([\d]+)\}/g;
                } else {
                    reg = /\{([\w]+)\}/g;
                }
                return str.replace(reg, function($0, $1) {
                    var value = arr[$1];
                    if (value !== undefined) {
                        return value;
                    } else {
                        return "";
                    }
                });
            },
            
            trim: function(str) {
                var str = str,
                str = str.replace(/^\s\s*/, ''),
                ws = /\s/,
                i = str.length;
                while (ws.test(str.charAt(--i)));
                    return str.slice(0, i + 1);
            }
        }
    }
/**
 * 构造方法
 */
function CalendarSelector(options) {
    this.initialize(options);
}
//直接在原型上扩展相关属性与方法,省去闭包的开销
CalendarSelector.prototype = {
        /**
         * 模板
         */
    template: {
            main: ['<div id="calendarS_{cid}" class="calendarSelector" style="position:absolute; z-index:999">',
                        '<div class="tips calendarPlugin" id="datepickerDays" style="display: block;">',
                            '<div class="calendarPlugin_top">',
                                '<a href="javascript:void(0);" class="i-calendarLeft" id="prevMonth"></a>',
                                '<a href="javascript:void(0);" class="calendarPlugin_time" id="dateDisplay">',
                                '</a>',
                                '<a href="javascript:void(0);" class="i-calendarRight" id="nextMonth"></a>',
                                '<input type="hidden" id="currentDate" />',
                            '</div>',
                            '<table class="calendarPluginTable">',
                                '<thead>',
                                    '<tr><th>日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th></tr>',
                                    '</thead>',
                                    '<tbody id="dateSelectorTBody"></tbody>',
                            '</table>',
                        '</div>',
                        '<div class="tips calendarPlugin" id="datepickerMonths" style="display: none;">',
                            '<div class="calendarPlugin_top">',
                                '<a href="javascript:void(0);" class="i-calendarLeft" id="prevYear"></a>',
                                '<a href="javascript:void(0);" class="calendarPlugin_timeS" id="yearDisplay">','</a>',
                                '<a href="javascript:void(0);" class="i-calendarRight" id="nextYear"></a>',
                            '</div>',
                            '<table class="calendarPluginTable_other">',
                                '<tbody id="monthSelectorTBody">',
                                '<tr>',
                                    '<td><div>1月</div></td>',
                                    '<td><div>2月</div></td>',
                                    '<td><div>3月</div></td>',
                                    '<td><div>4月</div></td>',
                                '</tr>',
                                '<tr>',
                                    '<td><div>5月</div></td>',
                                    '<td><div>6月</div></td>',
                                    '<td><div>7月</div></td>',
                                    '<td><div>8月</div></td>',
                                '</tr>',
                                '<tr>',
                                    '<td><div>9月</div></td>',
                                    '<td><div>10月</div></td>',
                                    '<td><div>11月</div></td>',
                                    '<td><div>12月</div></td>',
                                '</tr>',
                                '</tbody>',
                            '</table>',
                        '</div>',
                        '<div class="tips calendarPlugin" id="datepickerYears" style="display: none;">',
                            '<div class="calendarPlugin_top">',
                                '<a href="javascript:void(0);" class="i-calendarLeft" id="prevRangeYear"></a>',
                                '<span class="calendarPlugin_timeS" id="yearRangeDisplay">','</span>',
                                '<a href="javascript:void(0);" class="i-calendarRight" id="nextRangeYear"></a>',
                            '</div>',
                            '<table class="calendarPluginTable_other">',
                                '<tbody id="yearSelectorTBody"></tbody>',
                            '</table>',
                    '</div>'].join(''),

            date: ['<td class="{isSelectable}" title="农历{lunarDateStr}" date="{stringDate}">',
                        '<div class="{isToday}">',
                            '<span>{currentDay}</span>','<p>{lunarDate}</p>',
                        '</div>',
                    '</td>'
                    ].join(''),

            year: ['<td class="{isSelectableYear}" year="{stringYear}">',
                    '<div class="{isThisYear}">{stringYear}</div>',
                    '</td>'].join('')

        },
        monthNames: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
        monthNumber: ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
        minYear: 1901,
        maxYear: 2049,

        /**
         * 初始化
         * @param {Object} target 必选,需要绑定的元素对象,要求是jQuery对象
         * @param {Date} date 可选,初始化的时间
         * @param {String} dateFormat 可选,日期显示格式。如"yyyy-MM-dd"
         * @param {Function} callback 可选,选择日期后的回调
         * @param {String} direction 可选,日历选择器的展示位置。默认展示在元素target下方并左对齐
         */
        initialize: function(options) {
            var self = this;
            //参数初始化
            options || (options = {}); //容错
            
            if (!options.container) { //必须的参数检测
                throw new Error('[CalendarSelector] container is empty!');
            }
            this.target = $(options.container);
            //date参数转换和保存
            var date = options.date || new Date();

            if (typeof date !== 'object') {
                date = Tools.Date.parse(date);
            }

            this.date = date || new Date();
            
            this.dateFormat = options.dateFormat || "yyyy-MM-dd";
            this.callback = options.callback || function () {};
            this.direction = options.direction || "Auto";
            this.cid = Math.floor(Math.random() * 1000000000);
            this.pickerArea = $(Tools.Text.format(this.template.main, {cid: this.cid}));
            
            this.lastTargetTop = this.target.offset().top;
            this.target.on('click', function(event) {
                self.show();
            });
        },
    /**
     * 事件绑定
     */
    initEvents: function() {
        
        var self = this;
        
        this.prevMonthBtn.on('click', function(event) {
            self.addMonth(-1); 
        });

        ...

        //选择某年
        this.yearSelectorTbodyEl.on('click', 'td',function(event) {
            var tmpYear = $(event.currentTarget).attr("year");
            if (tmpYear > self.maxYear || tmpYear < self.minYear){
                return;
            } else {
                self.selectYear(tmpYear);    
            }
            
        });
        //选择某月
        this.monthTdBtn.on('click', function(event) {
            self.selectMonth(event);
        });
        // 选择某天
        this.dateSelectorTbodyEl.on('click', 'td',function(event) {
          self.changeDate($(event.currentTarget).attr("date"));
        });

        //注册mouseover与mouseout事件
        this.monthSelectorTbodyEl.on({
            mouseover: function() {
            var clsName = $(this).attr('class');
            if (!!!clsName) {
              $(this).addClass("onHoverDay");  
            }  
          },
          mouseout: function() {
            if (!$(this).hasClass('onDay')){
                $(this).removeClass("onHoverDay");
            }
          }
        },'td div');

        this.dateSelectorTbodyEl.on({
            mouseover: function() {
            if (!$(this).hasClass('onDay') ){
                $(this).addClass("onHoverDay");
            } 
          },
          mouseout: function() {
            if (!$(this).hasClass('onDay')){
                $(this).removeClass("onHoverDay");
            }
          }
        },'td div');

        this.yearSelectorTbodyEl.on({
            mouseover: function() {
            if (!$(this).hasClass('onDay') ){
                $(this).addClass("onHoverDay");
            } 
          },
          mouseout: function() {
            if (!$(this).hasClass('onDay')){
                $(this).removeClass("onHoverDay");
            }
          }
        },'td div');
    }
    // 更多代码见后文下载
}

以上只是大概给出设计的思路,即完全的MVC思想。抽离模板,渲染数据,最后是对DOM进行相关操作。这种做法使得代码层次结构清晰,而且便于维护。以下给出所有代码的下载地址,欢迎各路朋友拍砖。

代码下载地址 日期选择器

Fork me on GitHub