这是第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;
			width:210px;
			box-shadow:2px 2px 2px #ccc;
			padding:5px;
			font-size:12px;
		}
		.j-datepicker-container a{
			color:#333;
			text-decoration:none;
		}
		.j-datepicker-head{
			width: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;
			width: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;
			width:25px;
			background:#efefef;
			overflow:hidden;
		}
		.j-datepicker-time .time a{
			position:absolute;
			display:none;
			margin:0 auto;
			height:8px;
			width: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{
			width:23px;
			height:20px;
			line-height:20px;
			color:#333;
			border:1px solid #ccc;
			text-align:center;
		}
		.j-datepicker-time .time code em{
			display:block;
			width: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>

  

 posted on 2014-04-01 10:28  jacklau  阅读(660)  评论(0编辑  收藏  举报