• JS日期级联组件代码分析及demo


        最近研究下JS日期级联效果 感觉还不错,然后看了下kissy也正好有这么一个组件,也看了下源码,写的还不错,通过google最早是在2011年 淘宝的虎牙(花名)用原审JS写了一个(貌似据说是从YUI那边重构下的) 具体的可以看他的 博客园 , 感觉kissy组件源码 思路也是和YUI类似 所以我今天的基本思路也和他们的一样 只是通过自己分析下及用自己的方式包装下。

    基本原理

     1.传参中有 '年份下拉框dom节点', '月份下拉框dom节点', '天数下拉框dom节点', "开始日期","结束日期","默认日期"配置项

         1.如果开始传参日期为空 那么默认是从"1900-01-01"开始

         2.如果"结束日期为空" 那么默认结束日期为当前的时间。

         3. 如果默认日期为空 那么默认日期默认为当前的时间。

     2. 月份对应的天数可以直接写死 如:_dayInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ; 分别为1月份到12月份的各个月份的默认天数,当然还有2月份闰年29天的情况 待会在代码中会有判断的。

     3. 分别渲染出年份区间,月份区间,及相应的天数。(如果有默认的日期的话 且默认日期大于或者等于开始日期 且小于或者等于结束日期的话) 那么页面加载的时候 显示默认日期。

     4. 绑定change事件 当切换到不同年份的时候 月份和天数也要分别渲染出来。

    基本配置项如下:

       nodeYear
    '#year',    年份下拉框dom节点
     nodeMonth  '#month',  月份下拉框dom节点
     nodeDay  '#day',      日期下拉框dom节点
     dateStart   '',             开始日期(为空 默认日期从1900-01-01开始)
     dateEnd  '',             结束日期(可选 默认为空就为当前时间)
    dateDefault   ''             默认日期(可选 默认为空就为当前时间)

    对外提供的方法

    1. getDate()  返回当前时间,格式为yyyy-mm-dd

    2. getYear() 返回当前的年份

    3. getMonth() 返回当前的月份

    4. getDay() 返回当前月份中的天数.

    JSFiddle demo链接如下:

     查看demo 请点击我!

    下面代码分析如下:

    1. 初始化调用init方法:分别获取开始时间 结束时间 默认时间的 "年,月,天"。如下代码:

    // 开始时间可选 如果为空的话 那么默认开始时间是1900-01-01
            if(_config.dateStart != '') {
    
                this.startDate = {
                    y: new Date(_config.dateStart).getFullYear(),
                    m: new Date(_config.dateStart).getMonth() + 1,
                    d: new Date(_config.dateStart).getDate()
                };
            }else {
                var dateStart = '1900/01/01';
    
                this.startDate = {
                    y: new Date(dateStart).getFullYear(),
                    m: new Date(dateStart).getMonth() + 1,
                    d: new Date(dateStart).getDate()
                };
            }
            
            // dateEnd 默认为空 如果没有传入的话 就取当前的时间
            if(_config.dateEnd == '') {
                this.endDate = {
                    y: new Date().getFullYear(),
                    m: new Date().getMonth() + 1,
                    d: new Date().getDate()
                };
            }else {
                this.endDate = {
                    y: new Date(_config.dateEnd).getFullYear(),
                    m: new Date(_config.dateEnd).getMonth() + 1,
                    d: new Date(_config.dateEnd).getDate()
                };
            }
            
            // 默认时间可选 如果默认时间为空的话 那么就取当前的时间
            if(_config.dateDefault != '') {
                this.defaultDate = {
                    y: new Date(_config.dateDefault).getFullYear(),
                    m: new Date(_config.dateDefault).getMonth() + 1,
                    d: new Date(_config.dateDefault).getDate()
                };
            }else {
                this.defaultDate = {
                    y: new Date().getFullYear(),
                    m: new Date().getMonth() + 1,
                    d: new Date().getDate()
                };
            }
            // 判断时间是否合理
            if((Date.parse(self._changeFormat(_config.dateStart)) > Date.parse(self._changeFormat(_config.dateEnd))) || 
              (Date.parse(self._changeFormat(_config.dateDefault)) > Date.parse(self._changeFormat(_config.dateEnd)))){
                return;
            }

    2. 渲染下拉框的年份:调用 y = self._renderYear();这个方法。

        1. 获取年份的区间范围,获取方法就是:获取开始时间的年份 和 结束时的年份 如下代码:

    /*
     * 获取年份的范围 最小-最大
     * @method _getYearRange 
     * @return {min,max}
     */
    _getYearRange: function(){
          var self = this,
            _config = self.config;
        return {
            min: self.startDate.y,
            max: self.endDate.y
        }
    },

        2. 接着渲染年份,从最近的年份开始渲染,如果有默认的年份 且 满足条件的话 那么默认的年份显示出来。如下代码:

    /*
     * 渲染年份下拉框
     * @method _renderYear
     * private
     */
        _renderYear: function(){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            var nodeyear = $(_config.nodeYear)[0],
                y = self.defaultDate.y,
                range,
                option;
    
            if(nodeyear) {
                range = self._getYearRange();
                for(var i = range.max; i >= range.min; i--) {
                    option = new Option(i,i);
    
                    // 如果有默认年份的话
                    if(i == y) {
                        option.selected = true;
                    }
                    // 兼容所有浏览器 插入到最后
                    nodeyear.add(option,undefined);
                }
            }
            $(nodeyear).attr('year',y);
            return y;
        },

    3. 接着渲染月份 调用这个方法  y参数就是刚刚返回的年份  m = self._renderMonth(y);

        1. 同理 渲染月份也要获取月份的范围 默认都是从1月份到12月份 但是也有列外。比如如下2个判断。

    /*
         * 获取月份的范围
         * @method _getMonthRange
         * @param {y} Number
         */
        _getMonthRange: function(y){
            var self = this,
                _config = self.config;
            var startDate = self.startDate,
                endDate = self.endDate,
                min = 1,
                max = 12;
            /*
             * 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份
             * 因为如果开始是1900-05-01 如果默认的是 1900-03-02 那么最小月份肯定取得是5
             * 因为默认时间不可能小于开始时间
             */
            if(y == startDate.y) { // 开始年份
                min = startDate.m;
            }
            
            /*
             * 同理 如果默认年份等于2014-04-01 那么取得是当前的年份(endDate未传的情况下)
             * 那么最大的肯定取得是当前年份的 月份 不可能取的是4 因为只渲染出当前月份出来 
             * 后面的月份没有渲染出来
             */
            if(y == endDate.y) {
                max = endDate.m;
            }
            return {
                min: min,
                max: max
            }
        },

         2. 知道月份的范围后 然后根据上面的年份渲染相应的月份:代码如下:

    /*
         * 根据年份 渲染所有的月份
         * @method _renderMonth
         * @param {y} 年份
         */
        _renderMonth: function(y){
            var self = this,
                _config = self.config;
            var nodeMonth = $(_config.nodeMonth)[0],
                m = $(nodeMonth).attr('month') || self.defaultDate.m,
                range,
                option,
                t = false;
            if(nodeMonth) {
                range = self._getMonthRange(y);
                
                nodeMonth.innerHTML = '';
                for(var i = range.min; i <= range.max; i++) {
                    option = new Option(self.bitExpand(i),self.bitExpand(i));
                    
                    // 如果有默认的月份的话
                    if(i == m) {
                        option.selected = true;
                        m = i;
                        t = true;
                    }
                    // 兼容所有浏览器 插入到最后
                    nodeMonth.add(option,undefined);
                }
                if(!t) {
                    m = range.min;
                }
            }
            
            $(nodeMonth).attr('month',m);
            return m;
        },

         上面的代码 用了这句判断  m = $(nodeMonth).attr('month') || self.defaultDate.m, 默认情况下 也就是说页面一加载的时候 可以获取默认的月份,但是当我触发change事件后 我取的月份 是从m = $(nodeMonth).attr('month') 这个里面取得。上面代码 nodeMonth.innerHTML = ''; 也是为了change时候 请清空掉 然后重新生成的。

    4.  渲染天数 通过这个方法: self._renderDay(y,m);

         1. 渲染天数 同理也要获得相应的天数。调用_getDayRange方法。此方法中有判断是闰年的情况的。如下代码:

    /*
         * 获得天数的范围
         * @method _getDayRange
         * @param {y,m} {number,number}
         */
        _getDayRange: function(y,m){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            var startDate = self.startDate,
                endDate = self.endDate,
                min = 1,
                max;
    
            if(m) {
                if(m == 2) {
                    max = self._isLeapYear(y) ? 29 : 28;
                }else {
                    max = _cache._dayInMonth[m-1];
                }
                // 如果年月份都等于开始日期的话 那么min也等于开始日
                if(y == startDate.y && m == startDate.m) {
                    min = startDate.d;
                }
                // 如果年月份都等于结束日期的话 那么max也等于结束日
                if(y == endDate.y && m == endDate.m) {
                    max = endDate.d;
                }
            }
            return {
                min: min,
                max: max
            }
        },

         2.接着渲染天数的方法如下:

    _renderDay: function(y,m) {
            var self = this,
                _config = self.config;
            var nodeDay = $(_config.nodeDay)[0],
                d = $(nodeDay).attr('day') || self.defaultDate.d,
                range,
                option,
                t = false;
            if(nodeDay) {
                range = self._getDayRange(y,m);
    
                nodeDay.innerHTML = '';
                for(var i = range.min; i <= range.max; i++) {
                    option = new Option(self.bitExpand(i),self.bitExpand(i));
    
                    // 如果有默认的天数的话
                    if(i == d) {
                        option.selected = true;
                        d = i;
                        t = true;
                    }
                    // 兼容所有浏览器 插入到最后
                    nodeDay.add(option,undefined);
                }
                if(!t) {
                    d = range.min;
                }
            }
            
            $(nodeDay).attr('day',d);
            return d;
        },

     5 最后用绑定change事件 调用_bindEnv方法。如:

    /*
         * 绑定所有事件
         * @method _bindEnv
         * private
         */
        _bindEnv:function(){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            //年份改变
            $(_config.nodeYear).change(function(e){
                
                var y = e.target.value,
                    m = self._renderMonth(y);
                
                self._renderDay(y,m);
                $(_config.nodeYear).attr('year',y);
            });
            //月份改变
            $(_config.nodeMonth).change(function(e){
                
                var m = e.target.value,
                    y = $(_config.nodeYear).attr('year');
                
                self._renderDay(y,m);
                $(_config.nodeMonth).attr('month',m);
            });
    
            //日期改变
            $(_config.nodeDay).change(function(e){
                var d = e.target.value;
                $(_config.nodeDay).attr('day',d);
            });
        },

    HTML代码如下:

    <label>出生日期: </label>
        <select id="year"> </select><select id="month"> </select><select id="day"> </select><ul>
            <li><em>getDate</em> : <button id="testDate">日期</button><input id="textDate"/></li>
            <li><em>getYear</em> : <button id="testYear"></button><input id="textYear"/></li>
            <li><em>getMonth</em> : <button id="testMonth"></button><input id="textMonth"/></li>
            <li><em>getDay</em> : <button id="testDay"></button><input id="textDay"/></li>
        </ul>

    JS代码如下:

    /**
     * JS日期级联组件
     * @constructor DateCascade
     * @param {object} 可配置的对象
     * @time 2014-1-13
     * @author 879083421@qq.com
     */
     
     function DateCascade(options) {
        
        this.config = {
            nodeYear          : '#year',        // 年份下拉框dom
            nodeMonth         : '#month',       // 月份下拉框dom
            nodeDay           : '#day',         // 日期下拉框dom
            dateStart         : '',             // 开始日期
            dateEnd           : '',             // 结束日期(可选 默认为空就为当前时间)
            dateDefault       : ''              // 默认日期
        };
     
        this.cache = {
            _dayInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]  // 月份对应的天数
        };
        this.init(options);
     }
    
     DateCascade.prototype = {
    
        constructor: DateCascade,
    
        init: function(options) {
            
            this.config = $.extend(this.config,options || {});
            var self = this,
                _config = self.config,
                _cache = self.cache;
            
            var y,
                m;
    
            /* 开始时间 和 截至时间 默认时间*/
    
            // 开始时间可选 如果为空的话 那么默认开始时间是1900-01-01
            if(_config.dateStart != '') {
    
                this.startDate = {
                    y: new Date(_config.dateStart).getFullYear(),
                    m: new Date(_config.dateStart).getMonth() + 1,
                    d: new Date(_config.dateStart).getDate()
                };
            }else {
                var dateStart = '1900/01/01';
    
                this.startDate = {
                    y: new Date(dateStart).getFullYear(),
                    m: new Date(dateStart).getMonth() + 1,
                    d: new Date(dateStart).getDate()
                };
            }
            
            // dateEnd 默认为空 如果没有传入的话 就取当前的时间
            if(_config.dateEnd == '') {
                this.endDate = {
                    y: new Date().getFullYear(),
                    m: new Date().getMonth() + 1,
                    d: new Date().getDate()
                };
            }else {
                this.endDate = {
                    y: new Date(_config.dateEnd).getFullYear(),
                    m: new Date(_config.dateEnd).getMonth() + 1,
                    d: new Date(_config.dateEnd).getDate()
                };
            }
            
            // 默认时间可选 如果默认时间为空的话 那么就取当前的时间
            if(_config.dateDefault != '') {
                this.defaultDate = {
                    y: new Date(_config.dateDefault).getFullYear(),
                    m: new Date(_config.dateDefault).getMonth() + 1,
                    d: new Date(_config.dateDefault).getDate()
                };
            }else {
                this.defaultDate = {
                    y: new Date().getFullYear(),
                    m: new Date().getMonth() + 1,
                    d: new Date().getDate()
                };
            }
            // 判断时间是否合理
            if((Date.parse(self._changeFormat(_config.dateStart)) > Date.parse(self._changeFormat(_config.dateEnd))) || 
              (Date.parse(self._changeFormat(_config.dateDefault)) > Date.parse(self._changeFormat(_config.dateEnd)))){
                return;
            }
    
            // 渲染年份
            y = self._renderYear();
    
            // 渲染月份
            m = self._renderMonth(y);
    
            // 渲染天
            self._renderDay(y,m);
    
            // 所有绑定事件
            self._bindEnv();
        },
        /*
         * 渲染年份下拉框
         * @method _renderYear
         * private
         */
        _renderYear: function(){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            var nodeyear = $(_config.nodeYear)[0],
                y = self.defaultDate.y,
                range,
                option;
    
            if(nodeyear) {
                range = self._getYearRange();
                for(var i = range.max; i >= range.min; i--) {
                    option = new Option(i,i);
                    
                    // 如果有默认年份的话
                    if(i == y) {
                        option.selected = true;
                    }
                    
                    // 兼容所有浏览器 插入到最后
                    nodeyear.add(option,undefined);
                }
            }
            $(nodeyear).attr('year',y);
            return y;
        },
        /*
         * 根据年份 渲染所有的月份
         * @method _renderMonth
         * @param {y} 年份
         */
        _renderMonth: function(y){
            var self = this,
                _config = self.config;
            var nodeMonth = $(_config.nodeMonth)[0],
                m = $(nodeMonth).attr('month') || self.defaultDate.m,
                range,
                option,
                t = false;
            if(nodeMonth) {
                range = self._getMonthRange(y);
                
                nodeMonth.innerHTML = '';
                for(var i = range.min; i <= range.max; i++) {
                    option = new Option(self.bitExpand(i),self.bitExpand(i));
                    
                    // 如果有默认的月份的话
                    if(i == m) {
                        option.selected = true;
                        m = i;
                        t = true;
                    }
                    // 兼容所有浏览器 插入到最后
                    nodeMonth.add(option,undefined);
                }
                if(!t) {
                    m = range.min;
                }
            }
            
            $(nodeMonth).attr('month',m);
            return m;
        },
        _renderDay: function(y,m) {
            var self = this,
                _config = self.config;
            var nodeDay = $(_config.nodeDay)[0],
                d = $(nodeDay).attr('day') || self.defaultDate.d,
                range,
                option,
                t = false;
            if(nodeDay) {
                range = self._getDayRange(y,m);
    
                nodeDay.innerHTML = '';
                for(var i = range.min; i <= range.max; i++) {
                    option = new Option(self.bitExpand(i),self.bitExpand(i));
    
                    // 如果有默认的天数的话
                    if(i == d) {
                        option.selected = true;
                        d = i;
                        t = true;
                    }
                    // 兼容所有浏览器 插入到最后
                    nodeDay.add(option,undefined);
                }
                if(!t) {
                    d = range.min;
                }
            }
            
            $(nodeDay).attr('day',d);
            return d;
        },
        /*
         * 绑定所有事件
         * @method _bindEnv
         * private
         */
        _bindEnv:function(){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            //年份改变
            $(_config.nodeYear).change(function(e){
                
                var y = e.target.value,
                    m = self._renderMonth(y);
                
                self._renderDay(y,m);
                $(_config.nodeYear).attr('year',y);
            });
            //月份改变
            $(_config.nodeMonth).change(function(e){
                
                var m = e.target.value,
                    y = $(_config.nodeYear).attr('year');
                
                self._renderDay(y,m);
                $(_config.nodeMonth).attr('month',m);
            });
    
            //日期改变
            $(_config.nodeDay).change(function(e){
                var d = e.target.value;
                $(_config.nodeDay).attr('day',d);
            });
        },
        /*
         * 获取年份的范围 最小-最大
         * @method _getYearRange 
         * @return {min,max}
         */
        _getYearRange: function(){
            var self = this,
                _config = self.config;
            return {
                min: self.startDate.y,
                max: self.endDate.y
            }
        },
        /*
         * 获取月份的范围
         * @method _getMonthRange
         * @param {y} Number
         */
        _getMonthRange: function(y){
            var self = this,
                _config = self.config;
            var startDate = self.startDate,
                endDate = self.endDate,
                min = 1,
                max = 12;
            /*
             * 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份
             * 因为如果开始是1900-05-01 如果默认的是 1900-03-02 那么最小月份肯定取得是5
             * 因为默认时间不可能小于开始时间
             */
            if(y == startDate.y) { // 开始年份
                min = startDate.m;
            }
            
            /*
             * 同理 如果默认年份等于2014-04-01 那么取得是当前的年份(endDate未传的情况下)
             * 那么最大的肯定取得是当前年份的 月份 不可能取的是4 因为只渲染出当前月份出来 
             * 后面的月份没有渲染出来
             */
            if(y == endDate.y) {
                max = endDate.m;
            }
            return {
                min: min,
                max: max
            }
        },
        /*
         * 获得天数的范围
         * @method _getDayRange
         * @param {y,m} {number,number}
         */
        _getDayRange: function(y,m){
            var self = this,
                _config = self.config,
                _cache = self.cache;
            var startDate = self.startDate,
                endDate = self.endDate,
                min = 1,
                max;
    
            if(m) {
                if(m == 2) {
                    max = self._isLeapYear(y) ? 29 : 28;
                }else {
                    max = _cache._dayInMonth[m-1];
                }
                // 如果年月份都等于开始日期的话 那么min也等于开始日
                if(y == startDate.y && m == startDate.m) {
                    min = startDate.d;
                }
                // 如果年月份都等于结束日期的话 那么max也等于结束日
                if(y == endDate.y && m == endDate.m) {
                    max = endDate.d;
                }
            }
            return {
                min: min,
                max: max
            }
        },
        /*
         * 判断是否是闰年
         */
        _isLeapYear: function(y){
            return (y % 4 === 0 && y % 100 !== 0) || (y % 400 === 0);
        },
        /**
         * 是否是Date格式
         * @method _isDate
         * @param {Date} d
         * @private
         * @return {Boolean}
         */
        _isDate: function(d){
            return Object.prototype.toString.call(d) === '[object Date]' && d.toString() !== 'Invalid Date' && !isNaN(d);
        },
        /*
         * 小于10的数字加零
         * @method bitExpand
         */
        bitExpand: function(num) {
            var num = num * 1;
            if(/d/.test(num)) {
                if(num < 10) {
                    return '0' + num;
                }else {
                    return num;
                }
            }
        },
        /*
         * 判断开始日期 默认日期 结束日期的格式
         */
        _changeFormat: function(date) {
            return date.replace(/'-'/g,'/');    
        },
        /*
         * 获取日期
         */
        getDate: function(){
            var self = this,
                _config = self.config;
            var year = $(_config.nodeYear).attr('year'),
                month = $(_config.nodeMonth).attr('month'),
                day = $(_config.nodeDay).attr('day');
            
            return (year + '-' + self.bitExpand(month) + '-' + self.bitExpand(day));
        },
        /*
         * 获取年份
         */
        getYear: function(){
            var self = this,
                _config = self.config;
            var year = $(_config.nodeYear).attr('year');
            return year;
        },
        /*
         * 获取月份
         */
        getMonth: function(){
            var self = this,
                _config = self.config;
            var month = $(_config.nodeMonth).attr('month');
            return month;
        },
        /*
         * 获取天数
         */
        getDay: function(){
            var self = this,
                _config = self.config;
    
            var day = $(_config.nodeDay).attr('day');
            return day;
        }
     }
    View Code

    初始化方式如下:

    // 初始化
    $(function(){
        var date = new DateCascade({});
        $('#testDate').click(function(e){
            $('#textDate').val(date.getDate());
        });
    
        $('#testYear').click(function(e){                  $('#textYear').val(date.bitExpand(date.getYear()));
        });
    
        $('#testMonth').click(function(e){
                    $('#textMonth').val(date.bitExpand(date.getMonth()));
        });
    
        $('#testDay').click(function(e){
                    $('#textDay').val(date.bitExpand(date.getDay()));
        });
    });

    DEMO下载

  • 相关阅读:
    windows安装python2.7后的注册(registry)问题
    用python解析pdf中的文本与表格【pdfplumber的安装与使用】
    python pdfplumber用于pdf表格提取
    python xlsxwriter写excel并操作各种格式属性
    ShellExecute, WinExec, CreateProcess区别
    Python调用Windows外部程序
    pynput使用简单说明
    有关/proc/uptime这个文件里两个参数所代表的意义
    Beyond Compare 4 提示错误“这个授权密钥已被吊销”的解决办法
    Android: adb push apk 到 system/app 目录时报“remote Read-only file system”
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/3519039.html
Copyright © 2020-2023  润新知