您好,欢迎访问一九零五行业门户网

基于jQuery的日期选择控件_jquery

但是也有些问题,第一画日历有点慢,第二兼容性不太好ie only,第三它不是基于jquery的哈哈。
那还是老规矩,做之前先看下效果
这下是更酷的ext风格了。
从上图我们可以看出这个控件其实有两个视图一个日期月视图,还有一个是年月选择视图。
1:还是先从html入手
日期控件确定html其实还是比较简单,因为明摆着是列表的数据格式,当然主要是采用table了。
两个视图分别用两个div包裹,控制div的显示隐藏即可以切换视图了。完整的html结构大家可以用iedeveloper看一下demo的结构,我自己截了一个图
2:根据html和效果图编写css
其实因为是ext风格的,所以直接copy的ext的css和图片。。
css也就不分析了,直接上代码。
因为博客园的语法高亮不支持css,所以就不贴出来了,给个下载地址吧:
所有用到的图片:
3:搞定了css之后呢,就开始编写我们javascript了。
上来就是一个完整代码
复制代码 代码如下:
;(function($) {
var useragent = window.navigator.useragent.tolowercase();
$.browser.msie8 = $.browser.msie && /msie 8\.0/i.test(useragent);
$.browser.msie7 = $.browser.msie && /msie 7\.0/i.test(useragent);
$.browser.msie6 = !$.browser.msie8 && !$.browser.msie7 && $.browser.msie && /msie 6\.0/i.test(useragent);
date.prototype.format = function(format) {
var o = {
m+: this.getmonth() + 1,
d+: this.getdate(),
h+: this.gethours(),
h+: this.gethours(),
m+: this.getminutes(),
s+: this.getseconds(),
q+: math.floor((this.getmonth() + 3) / 3),
w: 0123456.indexof(this.getday()),
s: this.getmilliseconds()
};
if (/(y+)/.test(format)) {
format = format.replace(regexp.$1, (this.getfullyear() + ).substr(4 - regexp.$1.length));
}
for (var k in o) {
if (new regexp(( + k + )).test(format))
format = format.replace(regexp.$1,
regexp.$1.length == 1 ? o[k] :
(00 + o[k]).substr(( + o[k]).length));
}
return format;
};
function dateadd(interval, number, idate) {
number = parseint(number);
var date;
if (typeof (idate) == string) {
date = idate.split(/\d/);
eval(var date = new date( + date.join(,) + ));
}
if (typeof (idate) == object) {
date = new date(idate.tostring());
}
switch (interval) {
case y: date.setfullyear(date.getfullyear() + number); break;
case m: date.setmonth(date.getmonth() + number); break;
case d: date.setdate(date.getdate() + number); break;
case w: date.setdate(date.getdate() + 7 * number); break;
case h: date.sethours(date.gethours() + number); break;
case n: date.setminutes(date.getminutes() + number); break;
case s: date.setseconds(date.getseconds() + number); break;
case l: date.setmilliseconds(date.getmilliseconds() + number); break;
}
return date;
};
$.fn.datepicker = function(o) {
var def = {
weekstart: 0,
weekname: [日, 一, 二, 三, 四, 五, 六], //星期的格式
monthname: [一, 二, 三, 四, 五, 六, 七, 八, 九, 十, 十一, 十二], //月份的格式
monthp: 月,
year: new date().getfullyear(), //定义年的变量的初始值
month: new date().getmonth() + 1, //定义月的变量的初始值
day: new date().getdate(), //定义日的变量的初始值
today: new date(),
btnok: 确定 ,
btncancel: 取消 ,
btntoday: 今天,
inputdate: null,
onreturn: false,
version: 1.1,
applyrule: false, //function(){};return rule={startdate,endate};
showtarget: null,
picker:
};
$.extend(def, o);
var cp = $(#bbit_dp_container);
if (cp.length == 0) {
var cpha = [];
cpha.push();
if ($.browser.msie6) {
cpha.push('');
}
cpha.push();
//头哟
cpha.push(   九月 2009
);
cpha.push(
);
cpha.push();
//周
cpha.push();
//生成周
for (var i = def.weekstart, j = 0; j cpha.push(, def.weekname[i], );
if (i == 6) { i = 0; } else { i++; }
}
cpha.push(
);
//生成tbody,需要重新生成的
cpha.push(
);
//生成tbody结束
cpha.push(
);
cpha.push(, def.btntoday,
);
cpha.push(
);
//输出下来框
cpha.push();
cpha.push();
//1月,7月 按钮两个
cpha.push(, def.monthname[0], , def.monthname[6], );
cpha.push(
);
cpha.push();
cpha.push(, def.monthname[1], , def.monthname[7], );
cpha.push(
);
cpha.push();
cpha.push(, def.monthname[2], , def.monthname[8], );
cpha.push(
);
cpha.push();
cpha.push(, def.monthname[3], , def.monthname[9], );
cpha.push(
);
cpha.push();
cpha.push(, def.monthname[4], , def.monthname[10], );
cpha.push(
);
cpha.push();
cpha.push(, def.monthname[5], , def.monthname[11], );
cpha.push(
);
cpha.push();
cpha.push(, def.btnok, , def.btncancel, );
cpha.push(
);
cpha.push(
);
cpha.push(
);
cpha.push(
);
var s = cpha.join();
$(document.body).append(s);
var cp = $(#bbit_dp_container);
initevents();
}
function initevents() {
//1 today btn;
$(#bbit-dp-today).click(returntoday);
cp.click(returnfalse);
$(#bbit_dp_inner tbody).click(tbhandler);
$(#bbit_dp_leftbtn).click(prevm);
$(#bbit_dp_rightbtn).click(nextm);
$(#bbit_dp_ymbtn).click(showym);
$(#bbit-dp-mp).click(mpclick);
$(#bbit-dp-mp-prev).click(mpprevy);
$(#bbit-dp-mp-next).click(mpnexty);
$(#bbit-dp-mp-okbtn).click(mpok);
$(#bbit-dp-mp-cancelbtn).click(mpcancel);
}
function mpcancel() {
$(#bbit-dp-mp).animate({ top: -193 }, { duration: 200, complete: function() { $(#bbit-dp-mp).hide(); } });
return false;
}
function mpok() {
def.year = def.cy;
def.month = def.cm + 1;
def.day = 1;
$(#bbit-dp-mp).animate({ top: -193 }, { duration: 200, complete: function() { $(#bbit-dp-mp).hide(); } });
writecb();
return false;
}
function mpprevy() {
var y = def.ty - 10
def.ty = y;
rryear(y);
return false;
}
function mpnexty() {
var y = def.ty + 10
def.ty = y;
rryear(y);
return false;
}
function rryear(y) {
var s = y - 4;
var ar = [];
for (var i = 0; i ar.push(s + i);
ar.push(s + i + 5);
}
$(#bbit-dp-mp td.bbit-dp-mp-year).each(function(i) {
if (def.year == ar[i]) {
$(this).addclass(bbit-dp-mp-sel);
}
else {
$(this).removeclass(bbit-dp-mp-sel);
}
$(this).html( + ar[i] + ).attr(xyear, ar[i]);
});
}
function mpclick(e) {
var panel = $(this);
var et = e.target || e.srcelement;
var td = gettd(et);
if (td == null) {
return false;
}
if ($(td).hasclass(bbit-dp-mp-month)) {
if (!$(td).hasclass(bbit-dp-mp-sel)) {
var ctd = panel.find(td.bbit-dp-mp-month.bbit-dp-mp-sel);
if (ctd.length > 0) {
ctd.removeclass(bbit-dp-mp-sel);
}
$(td).addclass(bbit-dp-mp-sel)
def.cm = parseint($(td).attr(xmonth));
}
}
if ($(td).hasclass(bbit-dp-mp-year)) {
if (!$(td).hasclass(bbit-dp-mp-sel)) {
var ctd = panel.find(td.bbit-dp-mp-year.bbit-dp-mp-sel);
if (ctd.length > 0) {
ctd.removeclass(bbit-dp-mp-sel);
}
$(td).addclass(bbit-dp-mp-sel)
def.cy = parseint($(td).attr(xyear));
}
}
return false;
}
function showym() {
var mp = $(#bbit-dp-mp);
var y = def.year;
def.cy = def.ty = y;
var m = def.month - 1;
def.cm = m;
var ms = $(#bbit-dp-mp td.bbit-dp-mp-month);
for (var i = ms.length - 1; i >= 0; i--) {
var ch = $(ms[i]).attr(xmonth);
if (ch == m) {
$(ms[i]).addclass(bbit-dp-mp-sel);
}
else {
$(ms[i]).removeclass(bbit-dp-mp-sel);
}
}
rryear(y);
mp.css(top, -193).show().animate({ top: 0 }, { duration: 200 });
}
function gettd(elm) {
if (elm.tagname.touppercase() == td) {
return elm;
}
else if (elm.tagname.touppercase() == body) {
return null;
}
else {
var p = $(elm).parent();
if (p.length > 0) {
if (p[0].tagname.touppercase() != td) {
return gettd(p[0]);
}
else {
return p[0];
}
}
}
return null;
}
function tbhandler(e) {
var et = e.target || e.srcelement;
var td = gettd(et);
if (td == null) {
return false;
}
var $td = $(td);
if (!$(td).hasclass(bbit-dp-disabled)) {
var s = $td.attr(xdate);
var arrs = s.split(-);
cp.data(indata, new date(arrs[0], parseint(arrs[1], 10) - 1, arrs[2]));
returndate();
}
return false;
}
function returnfalse() {
return false;
}
function prevm() {
if (def.month == 1) {
def.year--;
def.month = 12;
}
else {
def.month--
}
writecb();
return false;
}
function nextm() {
if (def.month == 12) {
def.year++;
def.month = 1;
}
else {
def.month++
}
writecb();
return false;
}
function returntoday() {
cp.data(indata, new date());
returndate();
}
function returndate() {
var ct = cp.data(ctarget);
var ck = cp.data(cpk);
var re = cp.data(onreturn);
var ndate = cp.data(indata)
var ads = cp.data(ads);
var ade = cp.data(ade);
var dis = false;
if (ads && ndate dis = true;
}
if (ade && ndate > ade) {
dis = true;
}
if (dis) {
return;
}
if (re && jquery.isfunction(re)) {
re.call(ct[0], cp.data(indata));
}
else {
ct.val(cp.data(indata).format(yyyy-mm-dd));
}
ck.attr(isshow, 0);
cp.removedata(ctarget).removedata(cpk).removedata(indata).removedata(onreturn)
.removedata(ads).removedata(ade);
cp.css(visibility, hidden);
ct = ck = null;
}
function writecb() {
var tb = $(#bbit_dp_inner tbody);
$(#bbit_dp_ymbtn).html(def.monthname[def.month - 1] + def.monthp + + def.year);
var firstdate = new date(def.year, def.month - 1, 1);
var diffday = def.weekstart - firstdate.getday();
var showmonth = def.month - 1;
if (diffday > 0) {
diffday -= 7;
}
var startdate = dateadd(d, diffday, firstdate);
var enddate = dateadd(d, 42, startdate);
var ads = cp.data(ads);
var ade = cp.data(ade);
var bhm = [];
var tds = def.today.format(yyyy-mm-dd);
var indata = cp.data(indata);
var ins = indata != null ? indata.format(yyyy-mm-dd) : ;
for (var i = 1; i if (i % 7 == 1) {
bhm.push();
}
var ndate = dateadd(d, i - 1, startdate);
var tdc = [];
var dis = false;
if (ads && ndate dis = true;
}
if (ade && ndate > ade) {
dis = true;
}
if (ndate.getmonth() tdc.push(bbit-dp-prevday);
}
else if (ndate.getmonth() > showmonth) {
tdc.push(bbit-dp-nextday);
}
if (dis) {
tdc.push(bbit-dp-disabled);
}
else {
tdc.push(bbit-dp-active);
}
var s = ndate.format(yyyy-mm-dd);
if (s == tds) {
tdc.push(bbit-dp-today);
}
if (s == ins) {
tdc.push(bbit-dp-selected);
}
bhm.push(, ndate.getdate(), );
if (i % 7 == 0) {
bhm.push(
);
}
}
tb.html(bhm.join());
}
var datereg = /^(\d{1,4})(-|\/|.)(\d{1,2})\2(\d{1,2})$/;
return $(this).each(function() {
var obj = $(this).addclass(bbit-dp-input);
var picker = $(def.picker);
def.showtarget == null && obj.after(picker);
picker.click(function(e) {
var isshow = $(this).attr(isshow);
//先隐藏
var me = $(this);
if (cp.css(visibility) == visible) {
cp.css( visibility, hidden);
}
if (isshow == 1) {
me.attr(isshow, 0);
cp.removedata(ctarget).removedata(cpk).removedata(indata).removedata(onreturn);
return false;
}
var v = obj.val();
if (v != ) {
v = v.match(datereg);
}
if (v == null || v == ) {
def.year = new date().getfullyear();
def.month = new date().getmonth() + 1;
def.day = new date().getdate();
def.inputdate = null
}
else {
def.year = parseint(v[1], 10);
def.month = parseint(v[3], 10);
def.day = parseint(v[4], 10);
def.inputdate = new date(def.year, def.month - 1, def.day);
}
cp.data(ctarget, obj).data(cpk, me).data(indata, def.inputdate).data(onreturn, def.onreturn);
if (def.applyrule && $.isfunction(def.applyrule)) {
var rule = def.applyrule.call(obj, obj[0].id);
if (rule) {
if (rule.startdate) {
cp.data(ads, rule.startdate);
}
else {
cp.removedata(ads);
}
if (rule.enddate) {
cp.data(ade, rule.enddate);
}
else {
cp.removedata(ade);
}
}
}
else {
cp.removedata(ads).removedata(ade)
}
writecb();
$(#bbit-dp-t).height(cp.height());
var t = def.showtarget || obj;
var pos = t.offset();
var height = t.outerheight();
var newpos = { left: pos.left, top: pos.top + height };
var w = cp.width();
var h = cp.height();
var bw = document.documentelement.clientwidth;
var bh = document.documentelement.clientheight;
if ((newpos.left + w) >= bw) {
newpos.left = bw - w - 2;
}
if ((newpos.top + h) >= bh) {
newpos.top = pos.top - h - 2;
}
if (newpos.left newpos.left = 10;
}
if (newpos.top newpos.top = 10;
}
$(#bbit-dp-mp).hide();
newpos.visibility = visible;
cp.css(newpos);
//cp.show();
$(this).attr(isshow, 1);
$(document).one(click, function(e) {
me.attr(isshow, 0);
cp.removedata(ctarget).removedata(cpk).removedata(indata);
cp.css(visibility, hidden);
});
return false;
});
});
};
})(jquery);
那接着就是分析一下实现的主要过程和一些注意的要点:
首先还是套版化编写jquery控件的套子:
复制代码 代码如下:
;(function($) {
//也可以使用$.fn.extend(datepicker:function(o){})
$.fn.datepicker= function(o) {
}
})(jquery);
这样做的好处上篇已经讲过了 ,就不重述了
接着就是定义默认的参数,已在代码中添加了注释说明这些参数的意义,有几个参数是为了多语言而设置的,如weekname,monthname
复制代码 代码如下:
var def = {
weekstart: 0,//一周开始的是星期几0代表星期天
weekname: [日, 一, 二, 三, 四, 五, 六], //星期的格式
monthname: [一, 二, 三, 四, 五, 六, 七, 八, 九, 十, 十一, 十二], //月份的格式
monthp: 月,//月的后缀
year: new date().getfullyear(), //定义年的变量的初始值
month: new date().getmonth() + 1, //定义月的变量的初始值
day: new date().getdate(), //定义日的变量的初始值
today: new date(),//today
btnok: 确定 ,//确定按钮的文字
btncancel: 取消 ,//取消按钮的文字
btntoday: 今天, //今天按钮的文字
inputdate: null,//无用,只是在代码中会用它存放数据
onreturn: false,//当选择日期后回调的函数
version: 1.0,//版本
applyrule: false, //日期选择规则,可设置可选择的日期范围function(){};return rule={startdate,endate};
showtarget: null, //显示载体,日历展开式所依赖的对象,默认是对象本身
picker: //附加点击事件的对象
};
$.extend(def, o);//用传递过来的参数来填充默认
第二部自然是初始化月视图和年月选择视图的html了
复制代码 代码如下:
//给日期选择控件一个特殊的id,获取这个id的对象,判断如果对象存在,则直接使用
// 日期的html采用单例,即一个页面上只生成一份html
var cp = $(#bbit_dp_container);
if (cp.length == 0) {
var cpha = []; //老规矩还是用数组拼接html,最后用innerhtml的方式附加到容器,提升性能
cpha.push();
if ($.browser.msie6) { //如果是ie6弹出层遮盖select
cpha.push('');
}
cpha.push();
//头哟
cpha.push(   九月 2009
);
cpha.push(
);
cpha.push();
//周
cpha.push();
//生成周
for (var i = def.weekstart, j = 0; j cpha.push(, def.weekname[i], );
if (i == 6) { i = 0; } else { i++; }
}
.....//省略若干代码
cpha.push(
);
cpha.push();
cpha.push();
var s = cpha.join();
$(document.body).append(s); //添加到body中
cp = $(#bbit_dp_container); //再获取一遍
initevents(); //初始化事件
}
这里有一个关键点,就是日期的html输出和事件初始化只做一次,因为基本上一页上同时不会打开两个。还有就是生成html中有一些特殊的自定义属性哦,仔细看下就会发现的,这些属性在后面的时间处理中都有很大的作用。那么来看一下事件吧
复制代码 代码如下:
$(#bbit-dp-today).click(returntoday);//今天按钮的事件
cp.click(returnfalse);//阻止冒泡
$(#bbit_dp_inner tbody).click(tbhandler);//给月视图中间body添加click事件而不是给每个td添加
$(#bbit_dp_leftbtn).click(prevm);//上个月
$(#bbit_dp_rightbtn).click(nextm);//下个月
$(#bbit_dp_ymbtn).click(showym);//切换到年月视图
$(#bbit-dp-mp).click(mpclick);//年月视图的点击事件,同样用于分发
$(#bbit-dp-mp-prev).click(mpprevy);//上一年
$(#bbit-dp-mp-next).click(mpnexty);//下一年
$(#bbit-dp-mp-okbtn).click(mpok);//ok按钮的事件
$(#bbit-dp-mp-cancelbtn).click(mpcancel);//cancel按钮的事件
给每一个需要点击的元素加上事件哦,这里有两个地方比较特殊,一个事月视图的点击视图,传统的做法就是给每个td都加事件,但是这个时候我的td还没有呢,但是如果在每次生成td的时候来附加事件,那么就由影响性能,所以直接给容器加了click事件,通过对事件源的判断来分发事件,另外一个年月选择视图,也是和上面一样的逻辑,那么我们
就拿月视图的点击事件来分析一下,其实每一个td生成的时候都会注册一个xdate自定义属性 ,来看一下tbhandler函数
复制代码 代码如下:
function tbhandler(e) {
var et = e.target || e.srcelement; //找到事件源
var td = gettd(et); //事件源递归往上找td
if (td == null) {
return false;
}
var $td = $(td);
.
if (!$(td).hasclass(bbit-dp-disabled)) {如果不是禁用状态
var s = $td.attr(xdate);//获取td的自定义属性日期数据
var arrs = s.split(-);
cp.data(indata, new date(arrs[0], parseint(arrs[1], 10) - 1, arrs[2]));
returndate();//返回日期
.
}
return false;
}
所有的日期选择时间初始化好了(一次性的),接着就要给每一个的picker添加点击事件了
复制代码 代码如下:
return $(this).each(function() {
var obj = $(this).addclass(bbit-dp-input);//给input添加样式
var picker = $(def.picker);//获取picker对象
//如果showtarget不为null这将picker注册到input的后面
//否则用户自己处理picker的位置,即picker在页面上本身就已经存在
//大家可以看看示例中1,3调用的区别
def.showtarget == null && obj.after(picker);
picker.click(function(e) {
....//省略代码
});
picker的点击事件比较长,单独拿出来讲一下我想比较好,第一个要点是现实隐藏事件的处理,第二个是窗口边缘问题的处理,还有一个就是日期范围规则的处理。
复制代码 代码如下:
function(e) {
//获取当前是否显示
var isshow = $(this).attr(isshow);
var me = $(this);
//如果显示着,则隐藏,用于处理点击一下picker显示,再点击picker隐藏的逻辑
if (cp.css(visibility) == visible) {
cp.css( visibility, hidden);
}
//同样是如果显示着
if (isshow == 1) {
me.attr(isshow, 0);
//remover临时数据,因为是单例所以要表示当前是哪个input
cp.removedata(ctarget).removedata(cpk).removedata(indata).removedata(onreturn);
return false; //阻止冒泡
}
//如果隐藏着,获取input的值
var v = obj.val();
if (v != ) {
v = v.match(datereg);//验证一下格式是否正确
}
if (v == null || v == ) {//格式不正确或为空则用当前日期
def.year = new date().getfullyear();
def.month = new date().getmonth() + 1;
def.day = new date().getdate();
def.inputdate = null
}
else {
//否则使用input的日期
def.year = parseint(v[1], 10);
def.month = parseint(v[3], 10);
def.day = parseint(v[4], 10);
def.inputdate = new date(def.year, def.month - 1, def.day);
}
//注册临时数据
cp.data(ctarget, obj).data(cpk, me).data(indata, def.inputdate).data(onreturn, def.onreturn);
//调用规则,返回可选的日期范围
if (def.applyrule && $.isfunction(def.applyrule)) {
var rule = def.applyrule.call(obj, obj[0].id);
if (rule) {
if (rule.startdate) {
cp.data(ads, rule.startdate);
}
else {
cp.removedata(ads);
}
if (rule.enddate) {
cp.data(ade, rule.enddate);
}
else {
cp.removedata(ade);
}
}
}
else {
//不存在则删除限制
cp.removedata(ads).removedata(ade)
}
//画月日历内容td了
writecb();
$(#bbit-dp-t).height(cp.height());
//获取显示依附的对象
var t = def.showtarget || obj;
//获取对象的位置
var pos = t.offset();
//获取对象的高度
var height = t.outerheight();
//日期选择框的位置是依附对象的位置加上本身高度
var newpos = { left: pos.left, top: pos.top + height };
//以下都是处理窗口边界问题
var w = cp.width();
var h = cp.height();
var bw = document.documentelement.clientwidth;
var bh = document.documentelement.clientheight;
if ((newpos.left + w) >= bw) {
newpos.left = bw - w - 2;
}
if ((newpos.top + h) >= bh) {
newpos.top = pos.top - h - 2;
}
if (newpos.left newpos.left = 10;
}
if (newpos.top newpos.top = 10;
}
//强制默认是月日期视图
$(#bbit-dp-mp).hide();
newpos.visibility = visible;
cp.css(newpos); //移动到知道位置并显示
//cp.show();
$(this).attr(isshow, 1);
//给document注册单次的click事件,解决打开日期选择器后,点击其他位置,隐藏日期选择器的问题
$(document).one(click, function(e) {
me.attr(isshow, 0);
cp.removedata(ctarget).removedata(cpk).removedata(indata);
cp.css(visibility, hidden);
});
return false;//组织冒泡
}
其他一些代码都是日期操作的函数,如上月下月等就不做介绍了,大家如果对代码上又任何问题都可以留言,我一定解答,最后是示例了 .
第一个示例是老老实实的演示demo示例,有三种方式,也有调用方式的说明:http://jscs.cloudapp.net/controlssample/dpdemo
第二个示例是我写的日程管理控件中结合datepicker的应用(大家可以先看看这个)http://xuanye.cloudapp.net/
位置是: 和
是datepicker在我的创造中的应用,最后如果你觉得这边文章对你有所帮助,那就点击一下【推荐】?
代码打包脚本之家
其它类似信息

推荐信息