最近研究下JS日期级联效果 感觉还不错然后看了下kissy也正好有这么一个组件也看了下源码写的还不错通过google最早是在年 淘宝的虎牙(花名)用原审JS写了一个(貌似据说是从YUI那边重构下的) 具体的可以看他的 博客园 感觉kissy组件源码 思路也是和YUI类似 所以我今天的基本思路也和他们的一样 只是通过自己分析下及用自己的方式包装下
基本原理
传参中有 ;年份下拉框dom节点; ;月份下拉框dom节点; ;天数下拉框dom节点; "开始日期""结束日期""默认日期"配置项
如果开始传参日期为空 那么默认是从""开始
如果"结束日期为空" 那么默认结束日期为当前的时间
如果默认日期为空 那么默认日期默认为当前的时间
月份对应的天数可以直接写死 如_dayInMonth: [ ] ; 分别为月份到月份的各个月份的默认天数当然还有月份闰年天的情况 待会在代码中会有判断的
分别渲染出年份区间月份区间及相应的天数(如果有默认的日期的话 且默认日期大于或者等于开始日期 且小于或者等于结束日期的话) 那么页面加载的时候 显示默认日期
绑定change事件 当切换到不同年份的时候 月份和天数也要分别渲染出来
基本配置项如下
对外提供的方法
getDate() 返回当前时间格式为yyyymmdd
getYear() 返回当前的年份
getMonth() 返回当前的月份
getDay() 返回当前月份中的天数
JSFiddle demo链接如下
查看demo 请点击我!
下面代码分析如下
初始化调用init方法分别获取开始时间 结束时间 默认时间的 "年月天"如下代码
// 开始时间可选 如果为空的话 那么默认开始时间是 if(_configdateStart != ) { thisstartDate = { y: new Date(_configdateStart)getFullYear() m: new Date(_configdateStart)getMonth() + d: new Date(_configdateStart)getDate() }; }else { var dateStart = //; thisstartDate = { y: new Date(dateStart)getFullYear() m: new Date(dateStart)getMonth() + d: new Date(dateStart)getDate() }; } // dateEnd 默认为空 如果没有传入的话 就取当前的时间 if(_configdateEnd == ) { thisendDate = { y: new Date()getFullYear() m: new Date()getMonth() + d: new Date()getDate() }; }else { thisendDate = { y: new Date(_configdateEnd)getFullYear() m: new Date(_configdateEnd)getMonth() + d: new Date(_configdateEnd)getDate() }; } // 默认时间可选 如果默认时间为空的话 那么就取当前的时间 if(_configdateDefault != ) { thisdefaultDate = { y: new Date(_configdateDefault)getFullYear() m: new Date(_configdateDefault)getMonth() + d: new Date(_configdateDefault)getDate() }; }else { thisdefaultDate = { y: new Date()getFullYear() m: new Date()getMonth() + d: new Date()getDate() }; } // 判断时间是否合理 if((Dateparse(self_changeFormat(_configdateStart)) > Dateparse(self_changeFormat(_configdateEnd))) || (Dateparse(self_changeFormat(_configdateDefault)) > Dateparse(self_changeFormat(_configdateEnd)))){ return; }
渲染下拉框的年份调用 y = self_renderYear();这个方法
获取年份的区间范围获取方法就是获取开始时间的年份 和 结束时的年份 如下代码
/* * 获取年份的范围 最小最大 * @method _getYearRange * @return {minmax} */_getYearRange: function(){ var self = this _config = nfig; return { min: selfstartDatey max: selfendDatey }}
接着渲染年份从最近的年份开始渲染如果有默认的年份 且 满足条件的话 那么默认的年份显示出来如下代码
/* * 渲染年份下拉框 * @method _renderYear * private */ _renderYear: function(){ var self = this _config = nfig _cache = selfcache; var nodeyear = $(_confignodeYear)[] y = selfdefaultDatey range option; if(nodeyear) { range = self_getYearRange(); for(var i = rangemax; i >= rangemin; i) { option = new Option(ii); // 如果有默认年份的话 if(i == y) { optionselected = true; } // 兼容所有浏览器 插入到最后 nodeyearadd(optionundefined); } } $(nodeyear)attr(yeary); return y; }
接着渲染月份 调用这个方法 y参数就是刚刚返回的年份 m = self_renderMonth(y);
同理 渲染月份也要获取月份的范围 默认都是从月份到月份 但是也有列外比如如下个判断
/* * 获取月份的范围 * @method _getMonthRange * @param {y} Number */ _getMonthRange: function(y){ var self = this _config = nfig; var startDate = selfstartDate endDate = selfendDate min = max = ; /* * 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份 * 因为如果开始是 如果默认的是 那么最小月份肯定取得是 * 因为默认时间不可能小于开始时间 */ if(y == startDatey) { // 开始年份 min = startDatem; } /* * 同理 如果默认年份等于 那么取得是当前的年份(endDate未传的情况下) * 那么最大的肯定取得是当前年份的 月份 不可能取的是 因为只渲染出当前月份出来 * 后面的月份没有渲染出来 */ if(y == endDatey) { max = endDatem; } return { min: min max: max } }
知道月份的范围后 然后根据上面的年份渲染相应的月份代码如下
/* * 根据年份 渲染所有的月份 * @method _renderMonth * @param {y} 年份 */ _renderMonth: function(y){ var self = this _config = nfig; var nodeMonth = $(_confignodeMonth)[] m = $(nodeMonth)attr(month) || selfdefaultDatem range option t = false; if(nodeMonth) { range = self_getMonthRange(y); nodeMonthinnerHTML = ; for(var i = rangemin; i <= rangemax; i++) { option = new Option(selfbitExpand(i)selfbitExpand(i)); // 如果有默认的月份的话 if(i == m) { optionselected = true; m = i; t = true; } // 兼容所有浏览器 插入到最后 nodeMonthadd(optionundefined); } if(!t) { m = rangemin; } } $(nodeMonth)attr(monthm); return m; }
上面的代码 用了这句判断 m = $(nodeMonth)attr(;month;) || selfdefaultDatem 默认情况下 也就是说页面一加载的时候 可以获取默认的月份但是当我触发change事件后 我取的月份 是从m = $(nodeMonth)attr(;month;) 这个里面取得上面代码 nodeMonthinnerHTML = ;;; 也是为了change时候 请清空掉 然后重新生成的
渲染天数 通过这个方法 self_renderDay(ym);
渲染天数 同理也要获得相应的天数调用_getDayRange方法此方法中有判断是闰年的情况的如下代码
/* * 获得天数的范围 * @method _getDayRange * @param {ym} {numbernumber} */ _getDayRange: function(ym){ var self = this _config = nfig _cache = selfcache; var startDate = selfstartDate endDate = selfendDate min = max; if(m) { if(m == ) { max = self_isLeapYear(y) ? : ; }else { max = _cache_dayInMonth[m]; } // 如果年月份都等于开始日期的话 那么min也等于开始日 if(y == startDatey && m == startDatem) { min = startDated; } // 如果年月份都等于结束日期的话 那么max也等于结束日 if(y == endDatey && m == endDatem) { max = endDated; } } return { min: min max: max } }
接着渲染天数的方法如下
_renderDay: function(ym) { var self = this _config = nfig; var nodeDay = $(_confignodeDay)[] d = $(nodeDay)attr(day) || selfdefaultDated range option t = false; if(nodeDay) { range = self_getDayRange(ym); nodeDayinnerHTML = ; for(var i = rangemin; i <= rangemax; i++) { option = new Option(selfbitExpand(i)selfbitExpand(i)); // 如果有默认的天数的话 if(i == d) { optionselected = true; d = i; t = true; } // 兼容所有浏览器 插入到最后 nodeDayadd(optionundefined); } if(!t) { d = rangemin; } } $(nodeDay)attr(dayd); return d; }
最后用绑定change事件 调用_bindEnv方法如
/* * 绑定所有事件 * @method _bindEnv * private */ _bindEnv:function(){ var self = this _config = nfig _cache = selfcache; //年份改变 $(_confignodeYear)change(function(e){ var y = etargetvalue m = self_renderMonth(y); self_renderDay(ym); $(_confignodeYear)attr(yeary); }); //月份改变 $(_confignodeMonth)change(function(e){ var m = etargetvalue y = $(_confignodeYear)attr(year); self_renderDay(ym); $(_confignodeMonth)attr(monthm); }); //日期改变 $(_confignodeDay)change(function(e){ var d = etargetvalue; $(_confignodeDay)attr(dayd); }); }
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 * @author */ function DateCascade(options) { nfig = { nodeYear : #year // 年份下拉框dom nodeMonth : #month // 月份下拉框dom nodeDay : #day // 日期下拉框dom dateStart : // 开始日期 dateEnd : // 结束日期(可选 默认为空就为当前时间) dateDefault : // 默认日期 }; thiscache = { _dayInMonth: [ ] // 月份对应的天数 }; thisinit(options); } DateCascadeprototype = { constructor: DateCascade init: function(options) { nfig = $extend(nfigoptions || {}); var self = this _config = nfig _cache = selfcache; var y m; /* 开始时间 和 截至时间 默认时间*/ // 开始时间可选 如果为空的话 那么默认开始时间是 if(_configdateStart != ) { thisstartDate = { y: new Date(_configdateStart)getFullYear() m: new Date(_configdateStart)getMonth() + d: new Date(_configdateStart)getDate() }; }else { var dateStart = //; thisstartDate = { y: new Date(dateStart)getFullYear() m: new Date(dateStart)getMonth() + d: new Date(dateStart)getDate() }; } // dateEnd 默认为空 如果没有传入的话 就取当前的时间 if(_configdateEnd == ) { thisendDate = { y: new Date()getFullYear() m: new Date()getMonth() + d: new Date()getDate() }; }else { thisendDate = { y: new Date(_configdateEnd)getFullYear() m: new Date(_configdateEnd)getMonth() + d: new Date(_configdateEnd)getDate() }; } // 默认时间可选 如果默认时间为空的话 那么就取当前的时间 if(_configdateDefault != ) { thisdefaultDate = { y: new Date(_configdateDefault)getFullYear() m: new Date(_configdateDefault)getMonth() + d: new Date(_configdateDefault)getDate() }; }else { thisdefaultDate = { y: new Date()getFullYear() m: new Date()getMonth() + d: new Date()getDate() }; } // 判断时间是否合理 if((Dateparse(self_changeFormat(_configdateStart)) > Dateparse(self_changeFormat(_configdateEnd))) || (Dateparse(self_changeFormat(_configdateDefault)) > Dateparse(self_changeFormat(_configdateEnd)))){ return; } // 渲染年份 y = self_renderYear(); // 渲染月份 m = self_renderMonth(y); // 渲染天 self_renderDay(ym); // 所有绑定事件 self_bindEnv(); } /* * 渲染年份下拉框 * @method _renderYear * private */ _renderYear: function(){ var self = this _config = nfig _cache = selfcache; var nodeyear = $(_confignodeYear)[] y = selfdefaultDatey range option; if(nodeyear) { range = self_getYearRange(); for(var i = rangemax; i >= rangemin; i) { option = new Option(ii); // 如果有默认年份的话 if(i == y) { optionselected = true; } // 兼容所有浏览器 插入到最后 nodeyearadd(optionundefined); } } $(nodeyear)attr(yeary); return y; } /* * 根据年份 渲染所有的月份 * @method _renderMonth * @param {y} 年份 */ _renderMonth: function(y){ var self = this _config = nfig; var nodeMonth = $(_confignodeMonth)[] m = $(nodeMonth)attr(month) || selfdefaultDatem range option t = false; if(nodeMonth) { range = self_getMonthRange(y); nodeMonthinnerHTML = ; for(var i = rangemin; i <= rangemax; i++) { option = new Option(selfbitExpand(i)selfbitExpand(i)); // 如果有默认的月份的话 if(i == m) { optionselected = true; m = i; t = true; } // 兼容所有浏览器 插入到最后 nodeMonthadd(optionundefined); } if(!t) { m = rangemin; } } $(nodeMonth)attr(monthm); return m; } _renderDay: function(ym) { var self = this _config = nfig; var nodeDay = $(_confignodeDay)[] d = $(nodeDay)attr(day) || selfdefaultDated range option t = false; if(nodeDay) { range = self_getDayRange(ym); nodeDayinnerHTML = ; for(var i = rangemin; i <= rangemax; i++) { option = new Option(selfbitExpand(i)selfbitExpand(i)); // 如果有默认的天数的话 if(i == d) { optionselected = true; d = i; t = true; } // 兼容所有浏览器 插入到最后 nodeDayadd(optionundefined); } if(!t) { d = rangemin; } } $(nodeDay)attr(dayd); return d; } /* * 绑定所有事件 * @method _bindEnv * private */ _bindEnv:function(){ var self = this _config = nfig _cache = selfcache; //年份改变 $(_confignodeYear)change(function(e){ var y = etargetvalue m = self_renderMonth(y); self_renderDay(ym); $(_confignodeYear)attr(yeary); }); //月份改变 $(_confignodeMonth)change(function(e){ var m = etargetvalue y = $(_confignodeYear)attr(year); self_renderDay(ym); $(_confignodeMonth)attr(monthm); }); //日期改变 $(_confignodeDay)change(function(e){ var d = etargetvalue; $(_confignodeDay)attr(dayd); }); } /* * 获取年份的范围 最小最大 * @method _getYearRange * @return {minmax} */ _getYearRange: function(){ var self = this _config = nfig; return { min: selfstartDatey max: selfendDatey } } /* * 获取月份的范围 * @method _getMonthRange * @param {y} Number */ _getMonthRange: function(y){ var self = this _config = nfig; var startDate = selfstartDate endDate = selfendDate min = max = ; /* * 如果默认年份等于开始年份的话 那么月份最小取得是开始的月份 * 因为如果开始是 如果默认的是 那么最小月份肯定取得是 * 因为默认时间不可能小于开始时间 */ if(y == startDatey) { // 开始年份 min = startDatem; } /* * 同理 如果默认年份等于 那么取得是当前的年份(endDate未传的情况下) * 那么最大的肯定取得是当前年份的 月份 不可能取的是 因为只渲染出当前月份出来 * 后面的月份没有渲染出来 */ if(y == endDatey) { max = endDatem; } return { min: min max: max } } /* * 获得天数的范围 * @method _getDayRange * @param {ym} {numbernumber} */ _getDayRange: function(ym){ var self = this _config = nfig _cache = selfcache; var startDate = selfstartDate endDate = selfendDate min = max; if(m) { if(m == ) { max = self_isLeapYear(y) ? : ; }else { max = _cache_dayInMonth[m]; } // 如果年月份都等于开始日期的话 那么min也等于开始日 if(y == startDatey && m == startDatem) { min = startDated; } // 如果年月份都等于结束日期的话 那么max也等于结束日 if(y == endDatey && m == endDatem) { max = endDated; } } return { min: min max: max } } /* * 判断是否是闰年 */ _isLeapYear: function(y){ return (y % === && y % !== ) || (y % === ); } /** * 是否是Date格式 * @method _isDate * @param {Date} d * @private * @return {Boolean} */ _isDate: function(d){ return ObjectprototypetoStringcall(d) === [object Date] && dtoString() !== Invalid Date && !isNaN(d); } /* * 小于的数字加零 * @method bitExpand */ bitExpand: function(num) { var num = num * ; if(/d/test(num)) { if(num < ) { return + num; }else { return num; } } } /* * 判断开始日期 默认日期 结束日期的格式 */ _changeFormat: function(date) { return datereplace(//g/); } /* * 获取日期 */ getDate: function(){ var self = this _config = nfig; var year = $(_confignodeYear)attr(year) month = $(_confignodeMonth)attr(month) day = $(_confignodeDay)attr(day); return (year + + selfbitExpand(month) + + selfbitExpand(day)); } /* * 获取年份 */ getYear: function(){ var self = this _config = nfig; var year = $(_confignodeYear)attr(year); return year; } /* * 获取月份 */ getMonth: function(){ var self = this _config = nfig; var month = $(_confignodeMonth)attr(month); return month; } /* * 获取天数 */ getDay: function(){ var self = this _config = nfig; var day = $(_confignodeDay)attr(day); return day; } }
初始化方式如下
// 初始化$(function(){ var date = new DateCascade({}); $(#testDate)click(function(e){ $(#textDate)val(dategetDate()); }); $(#testYear)click(function(e){ $(#textYear)val(datebitExpand(dategetYear())); }); $(#testMonth)click(function(e){ $(#textMonth)val(datebitExpand(dategetMonth())); }); $(#testDay)click(function(e){ $(#textDay)val(datebitExpand(dategetDay())); });});
DEMO下载