这是第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>