这是第n次重构了,有不少性能的优化提升。
PS:
1.支持各种范围指定
2.支持选中回调
3.支持时间选定(滑轮效果,你懂的)
4.支持多面板
需要详细源码请回复!
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>title</title> <link href="css/main.css"/> <style type="text/css"> body,ul{ padding:0; margin:0; } .j-datepicker-container{ position:absolute; border:1px solid #efefef; background:#fff; 210px; box-shadow:2px 2px 2px #ccc; padding:5px; font-size:12px; } .j-datepicker-container a{ color:#333; text-decoration:none; } .j-datepicker-head{ 175px; margin:0 0 0 15px; } .j-datepicker-head a:hover{ background:#efefef; } .j-datepicker-content{ padding:5px 2px; margin:5px 0; border-top:1px solid #efefef; border-bottom:1px solid #efefef; overflow:hidden; *zoom:1; } .j-datepicker-content .other{ margin:0 0 0 5px; padding:0 0 0 5px; border-left:1px solid #efefef; } .j-datepicker-content table{ float:left; } .j-datepicker-content th span, .j-datepicker-content td span, .j-datepicker-content td a{ display:block; 25px; height:20px; line-height:20px; text-align:center; border-radius:30%; } .j-datepicker-content td a:hover{ color:#fff; background:#333; } .j-datepicker-content td a.today, .j-datepicker-content td a.today:hover{ color:#fff; background:green; font-weight:bold; } .j-datepicker-content td span{ color:#ccc; } .j-datepicker-content th{ background-color:#efefef; } .j-datepicker-time{ overflow:hidden; *zoom:1; } .j-datepicker-time span{ float:left; text-align:center; height:25px; line-height:25px; } .j-datepicker-time .time{ position:relative; 25px; background:#efefef; overflow:hidden; } .j-datepicker-time .time a{ position:absolute; display:none; margin:0 auto; height:8px; 8px; line-height:8px; font-size:9px; background:#999; color:#fff; } .j-datepicker-time .time .up{ top:0; left:0; } .j-datepicker-time .time .down{ top:0; right:0; } .j-datepicker-time .time code{ display:block; } .j-datepicker-time .time code input{ 23px; height:20px; line-height:20px; color:#333; border:1px solid #ccc; text-align:center; } .j-datepicker-time .time code em{ display:block; 25px; height:25px; line-height:25px; color:#666; font-weight:bold; font-style:normal; } .j-datepicker-time span.right{ float:right; } .j-datepicker-time span button{ background:#efefef; color:#333; cursor:pointer; margin:0 0 0 10px; border:1px solid #ccc; } .j-datepicker-time .j-datepicker-today{ color:#fff; background:green; } </style> </head> <body> <div class="container"> <br><br><br><br> 单个面板:<input type="text" class="j-datepicker"> 2个面板:<input type="text" class="j-datepicker2"> 3个面板:<input type="text" class="j-datepicker3"> <br><br><br><br> <br><br><br><br> 限制起始年份:<input type="text" class="j-datepicker4"> 限制结束年份:<input type="text" class="j-datepicker5"> 终极限制:<input type="text" class="j-datepicker6"> </div> <script src="jquery.js"></script> <script src="base.js"></script> <script> (function($){ /** * 日历组件 * 写一个日历,有两个日历组合在一起,能选择范围的 * 要用注释 * 能选择当前日,有格式化处理 如果带时钟(类似中奖时转轮的样式),就加分 */ var Datepicker = DH.Base.create({ config : { weeks : ['日', '一', '二', '三', '四', '五', '六'], //星期 years : [-10, 10], // 年份显示范围 days : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], // 每月天数, format : 'yyyy-MM-dd', // 文本框日期格式 scope : null, // 显示日期范围 display : 1 // 显示日期面板个数 }, tpl : { main : '<div id="date-picker-<%=id%>" class="j-datepicker-container" style="display:none;"> <div class="j-datepicker-inner"> <div class="j-datepicker-head"> <a href="javascript:;" class="j-pmonth" title="上一月"><<</a> <select class="j-datepicker-year"></select> 年 <select name="" id="" class="j-datepicker-month"></select> 月 <a href="javascript:;" class="j-nmonth" title="下一月">>></a> </div> <div class="j-datepicker-content"> </div> <div class="j-datepicker-foot"> <div class="j-datepicker-time"> <span class="time"> <a href="javascript:;" class="j-datepicker-up up">-</a> <a href="javascript:;" class="j-datepicker-down down">+</a> <code class="j-datepicker-time-edit" style="display:none;"><input class="etime" type="text" /></code> <code class="j-datepicker-hour j-datepicker-time-list"></code> </span> <span>时</span> <span class="time"> <a href="javascript:;" class="j-datepicker-up up">-</a> <a href="javascript:;" class="j-datepicker-down down">+</a> <code class="j-datepicker-time-edit" style="display:none;"><input class="etime" type="text" /></code> <code class="j-datepicker-min j-datepicker-time-list"></code> </span> <span>分</span> <span class="time"> <a href="javascript:;" class="j-datepicker-up up">-</a> <a href="javascript:;" class="j-datepicker-down down">+</a> <code class="j-datepicker-time-edit" style="display:none;"><input class="etime" type="text" /></code> <code class="j-datepicker-sec j-datepicker-time-list"></code> </span> <span><button class="j-datepicker-settime">确定</button></span> <span class="right"><button class="j-datepicker-today">今天</button></span> </div> </div> </div> </div>', display : '<table> <thead class="j-datepicker-weeks"></thead> <tbody class="j-datepicker-days"></tbody> </table>', weeks : '<tr><%=list%></tr>', week : '<th><span><%=text%></span></th>', days : '<tr><%=list%></tr>', day : '<td><a class="j-datepicker-day<%=istoday%>" data-y="<%=year%>" data-m="<%=month%>" href="javascript:;"><%=text%></a></td>', day_disabled : '<td><span><%=text%></span></td>', hour : '<em><%=text%></em>' }, init : function(){ this.date = new Date; var self = this, id = (this.date - 0) + Math.ceil(Math.random() * 1000), el = this.tmpl(this.tpl.main, {id : id}) ; this.current = {}; $(function(){ $(document.body).append(el); self.el = $('#date-picker-' + id); self.initElements(); self.initEvents(); }); }, initElements : function(){ this.elContent = this.el.find('.j-datepicker-content'); this.elYear = this.el.find('.j-datepicker-year'); this.elMonth = this.el.find('.j-datepicker-month'); this.elHour = this.el.find('.j-datepicker-hour'); this.elMin = this.el.find('.j-datepicker-min'); this.elSec = this.el.find('.j-datepicker-sec'); }, initEvents : function(){ this.el.on('click', function(){ return false; }); this.el .on('change', '.j-datepicker-year', this.proxy(this.changeYear)) .on('change', '.j-datepicker-month', this.proxy(this.changeMonth)) .on('click', '.j-datepicker-day', this.proxy(this.clickDay)) .on('click', '.j-pmonth', this.proxy(this.pmonth)) .on('click', '.j-nmonth', this.proxy(this.nmonth)) .on('click', '.j-datepicker-today', this.proxy(this.setToday)) .on('mouseenter', '.time', this.proxy(this.enterTime)) .on('mouseleave', '.time', this.proxy(this.leaveTime)) .on('click', '.j-datepicker-up', this.proxy(this.goUp)) .on('click', '.j-datepicker-down', this.proxy(this.goDown)) .on('click', '.j-datepicker-time-list', this.proxy(this.showEdit)) .on('click', '.j-datepicker-settime', this.proxy(this.setTime)) .on('blur', '.etime', this.proxy(this.resetTime)) ; this.setHours(); this.setMin(); this.setSec(); }, /** * 绑定到文本框 */ wrap : function(el, options){ if(!el) return; this.wel = $(el); this.wo = {}; this.wel .on('focus', this.proxy(this.focus)) .on('click', function(){ return false; }) ; $(document).on('click', this.proxy(this.hide)); $.extend(this.wo, typeof options === 'object' && options); }, /** * 焦点触发事件 */ focus : function(e){ var $e = $(e.target), h = $e.outerHeight(true), os = $e.offset(), str = $e.val() ; this.el .show() .css({ left : os.left, top : os.top + h }); this.setYears(); this.setMonths(); if(str && (str = this.unformat(str))) { this.setDisplay(str.year, str.month); this.selectYear(str.year); this.selectMonth(str.month); }else{ this.setDisplay(); } }, /** * 取得今天 * @return {[object]} */ getToday : function(){ var $e = this.wel, year = +this.date.getFullYear(), month = +this.date.getMonth() + 1, day = +this.date.getDate() ; return { year : year, month : month, day : day } }, /** * 设置为今天 */ setToday : function(){ var date = this.getToday(); this.setVal(this.format(date.year, date.month, date.day)); }, /** * 是否是闰年 */ isLeap : function(year){ return (year % 4 === 0 && year % 100 !== 0) || (year % 100 === 0 && year % 400 === 0) }, changeYear : function(e){ var $e = $(e.target), year = +$e.val(), month = +this.elMonth.val() ; this.setDisplay(year, month); this.setYears(year); }, setYears : function(year){ var $year = this.elYear, year = year === undefined ? this.date.getFullYear() : year, scope = this.wo.years || this.config.years ; $year.empty(); for(var i = year + scope[0]; i < year + scope[1]; i++){ $year.append('<option value="'+ i +'">'+ i +'</option>'); } this.selectYear(year); }, selectYear : function(year){ year = year === undefined ? this.date.getFullYear() : year; this.elYear.val(year); }, changeMonth : function(e){ var $e = $(e.target), month = +$e.val(), year = +this.elYear.val() ; this.setDisplay(year, month); }, setMonths : function(){ var $month = this.elMonth; $month.empty(); for(var i = 0; i < 12; i++){ $month.append('<option value="'+ i +'">'+ (i + 1) +'</option>'); } this.selectMonth(); }, selectMonth : function(month){ month = month === undefined ? this.date.getMonth() : month; this.elMonth.val(month); }, setWeeks : function($display){ var self = this, $weeks = $display.find('.j-datepicker-weeks'), config = this.config, tmpl = this.tmpl, tpl = this.tpl, list = [] ; $weeks.empty(); $(this.config.weeks).each(function(m, n){ list.push(tmpl(tpl.week, {text : n})); }); $weeks.append(tmpl(tpl.weeks, {list : list.join('')})); }, getDays : function(year, month){ return month === 1 ? (this.isLeap(year) ? 29 : 28) : (this.config.days[month]); }, getDay : function(year, month){ var date = new Date(year + '/' + (month + 1) + '/1'); return date.getDay(); }, /** * 设置日期面板 */ setDisplay : function(year, month){ var $content = this.elContent, tyear = this.date.getFullYear(), tmonth = this.date.getMonth(), year = year === undefined ? tyear : year, month = month === undefined ? tmonth : month, display = this.wo.display || this.config.display, i = 0 ; $content.empty(); for(; i < display; i++){ var $display = $(this.tpl.display); $content.append($display); this.setWeeks($display); if(month + i > 11){ month = 0; year += 1; }else{ month += i; } this.setDays($display, year, month); i > 0 && $display.addClass('other'); } this.el.css({ width : 215 * i }); }, /** * 设置面板内日期 */ setDays : function($display, year, month){ var self = this, scope = this.wo.scope || this.config.scope, $days = $display.find('.j-datepicker-days'), tmpl = this.tmpl, tpl = this.tpl, tyear = this.date.getFullYear(), tmonth = this.date.getMonth(), tday = this.date.getDate(), days = this.getDays(year, month), day = this.getDay(year, month), list = [], pyear = year, pmonth = month - 1 < 0 ? (pyear -= 1, 11) : month - 1, pdays = this.getDays(pyear, pmonth), nyear = year, nmonth = month + 1 > 11 ? (nyear += 1, 0) : month + 1 ndays = this.getDays(nyear, nmonth), html = '', canBegin = true, canEnd = true ; $days.empty(); day = day === 0 ? 7 : day; for(var i = pdays - day; i < pdays; i++){ list.push(tmpl(tpl.day_disabled, {text : i + 1})); } if(scope){ if(scope.begin && scope.begin.year && year < scope.begin.year){ canBegin = false; }else if(scope.begin && scope.begin.month && year >= scope.begin.year && month < scope.begin.month){ canBegin = false; } if(scope.end && scope.end.year && year > scope.end.year){ canEnd = false; }else if(scope.end && scope.end.month && year <= scope.end.year && month > scope.end.month){ canEnd = false; } } for(var i = 0; i < days; i++){ var can = true; if(canBegin && canEnd){ if(scope && scope.begin && scope.begin.month && month === scope.begin.month && (i + 1) < scope.begin.day || scope && scope.end && scope.end.month && month === scope.end.month && (i + 1) > scope.end.day){ can = false; } }else{ can = false; } // can 是否符合条件 if(can){ list.push(tmpl(tpl.day, {istoday : (year === tyear && month === tmonth && (i + 1) === tday ? ' today' : ''),text : i + 1, year : year, month : month})); }else{ list.push(tmpl(tpl.day_disabled, {text : i + 1})); } } for(var i = 0, len = 42 - list.length; i < len; i++){ list.push(tmpl(tpl.day_disabled, {text : i + 1})); } html += '<tr>'; $(list).each(function(m, n){ if(m !== 0 && m % 7 === 0){ html += '</tr><tr>'; } html += n; }); html += '</tr>'; $days.append(html); }, /** * 上一月 */ pmonth : function(){ var year = +this.elYear.val(), month = +this.elMonth.val() ; month = month - 1 < 0 ? (year -= 1, 11) : month - 1; this.setDisplay(year, month); this.setYears(year); this.selectMonth(month + ''); }, /** * 下一月 */ nmonth : function(){ var year = +this.elYear.val(), month = +this.elMonth.val() ; month = month + 1 > 11 ? (year += 1, 0) : month + 1; this.setDisplay(year, month); this.setYears(year); this.selectMonth(month + ''); }, /** * 点击日期事件 */ clickDay : function(e){ var $e = $(e.target), y = +$e.attr('data-y'), m = +$e.attr('data-m') + 1, d = +$e.text() ; this.setVal(this.format(y, m, d)); }, /** * 显示时间调整器 */ enterTime : function(e){ var $e = $(e.currentTarget); $e.find('.up').css('display', 'block'); $e.find('.down').css('display', 'block'); }, /** * 隐藏时间调整器 */ leaveTime : function(e){ var $e = $(e.currentTarget); $e.find('.up').hide(); $e.find('.down').hide(); }, /** * 显示时间编辑框 */ showEdit : function(e){ var $e = $(e.currentTarget), $p = $e.closest('.time'), val = +$p.attr('data-index') ; $p.find('.j-datepicker-time-edit') .show() .find('.etime') .val(val) .focus() ; $e.hide(); }, /** * 隐藏时间编辑框 */ hideEdit : function($time){ var $e = $time; $e.find('.j-datepicker-time-edit').hide(); $e.find('.j-datepicker-time-list').show(); }, /** * 重置时间 */ resetTime : function(e){ var $e = $(e.target), $time = $e.closest('.time'), $list = $time.find('.j-datepicker-time-list'), scope = +$time.attr('data-scope'), val = +$e.val() ; if(/^d+$/.test(val) && val <= scope && val >= 0){ $time.attr('data-index', val); this.goTime($list, val); } $e.val(''); this.hideEdit($time); }, goUp : function(e){ var $e = $(e.target), $time = $e.closest('.time') ; this.timeUp($time); }, goDown : function(e){ var $e = $(e.target), $time = $e.closest('.time') ; this.timeDown($time); }, timeUp : function($time){ var $e = $time, $list = $e.find('.j-datepicker-time-list'), scope = +$e.attr('data-scope'), index = +$e.attr('data-index') ; index = --index < 0 ? scope : index--; this.goTime($list, index); $e.attr('data-index', index); }, timeDown : function($time){ var $e = $time, $list = $e.find('.j-datepicker-time-list'), scope = +$e.attr('data-scope'), index = +$e.attr('data-index') ; index = ++index > scope ? 0 : index++; this.goTime($list, index); $e.attr('data-index', index); }, goTime : function($list, time){ var index = time; $list.animate({ marginTop : -(index * 25) }, 200); }, setHours : function(){ var $e = this.elHour, $time = $e.closest('.time'), tmpl = this.tmpl, tpl = this.tpl ; for(var i = 0; i < 24; i++){ $e.append(tmpl(tpl.hour, { text : i < 10 ? '0' + i : i })); } $time.attr('data-index', 0); $time.attr('data-scope', 23); }, setMin : function(){ var $e = this.elMin, $time = $e.closest('.time'), tmpl = this.tmpl, tpl = this.tpl ; for(var i = 0; i < 60; i++){ $e.append(tmpl(tpl.hour, { text : i < 10 ? '0' + i : i })); } $time.attr('data-index', 0); $time.attr('data-scope', 59); }, setSec : function(){ var $e = this.elSec, $time = $e.closest('.time'), tmpl = this.tmpl, tpl = this.tpl ; for(var i = 0; i < 60; i++){ $e.append(tmpl(tpl.hour, { text : i < 10 ? '0' + i : i })); } $time.attr('data-index', 0); $time.attr('data-scope', 59); }, setTime : function(){ var hour = this.elHour.closest('.time').attr('data-index'), min = this.elMin.closest('.time').attr('data-index'), sec = this.elSec.closest('.time').attr('data-index'), date = this.wel.val(), today = this.getToday() ; if(date === ''){ date = this.format(today.year, today.month, today.day); }else{ date = date.replace(/ d{2}:d{2}:d{2}$/, ''); } hour = hour < 10 ? '0' + hour : hour; min = min < 10 ? '0' + min : min; sec = sec < 10 ? '0' + sec : sec; date += ' ' + hour + ':' + min + ':' + sec; this.setVal(date); }, /** * 日期格式化,这里还可以扩展,比如yyyy-M-d(2014-3-21) */ format : function(year, month, day){ var result = this.wo.format || this.config.format; result = result .replace('yyyy', year) .replace('MM', (month + 1 < 10 ? '0' + month : month)) .replace('dd', (day < 10 ? '0' + day : day)) ; return result; }, /** * 日期反格式化,这里还可以扩展,比如yyyy-M-d(2014-3-21) */ unformat : function(str){ if(!str) return ''; var re = this.wo.format || this.config.format, m ; re = re .replace(/([-\_+])/g, '\$1') .replace('yyyy', '(\d+)') .replace('MM', '(\d+)') .replace('dd', '(\d+)') ; re = new RegExp(re); m = str.match(re); if(!m || !m.length) return ''; return { year : +m[1], month : +m[2] - 1, day : +m[3] } }, /** * 隐藏日期控件 */ hide : function(){ this.el.hide(); }, /** * 将最终结果赋值给文本框 */ setVal : function(str){ this.wel.val(str); this.hide(); this.trigger('setval', this.wel, str); // 触发自定义观察事件 }, reset : function(options){ this.wo = options; } }); var dp = new Datepicker().wrap('.j-datepicker', { format : 'yyyy年MM月dd日' }); var dp2 = new Datepicker().wrap('.j-datepicker2', { format : 'yyyy-MM-dd', display : 2 }); var dp3= new Datepicker().wrap('.j-datepicker3', { format : 'yyyy-MM-dd', display : 3 }); var dp4 = new Datepicker().wrap('.j-datepicker4', { format : 'yyyy年MM月dd日', scope : { begin : { year : 2014 } } }); var dp5 = new Datepicker().wrap('.j-datepicker5', { format : 'yyyy年MM月dd日', scope : { end : { year : 2014 } } }); var dp6 = new Datepicker().wrap('.j-datepicker6', { format : 'yyyy/MM/dd', display : 2, scope : { begin : { year : 2014, month : 2, // 月份从0开始 day : 15 }, end : { year : 2014, month : 5, day : 15 } } }); })(window.jQuery); </script> </body> </html>