打造基于jQuery的日期选择控件

终于把jQuery拼写正确了哈,哈哈javascript也是区分大小写的,所以确实不能写错,今天我来和大家分享的是日期选择控件的实现,功能也许不够强大,但是能够满足需求。

我之前也写过(正确的说是改过一个日期选择控件,点击这里查看),看下截图哈,也很酷的,windows风格哦。

但是也有些问题,第一画日历有点慢,第二兼容性不太好IE Only,第三它不是基于jQuery的哈哈。

那还是老规矩,做之前先看下效果

image  image

这下是更酷的Ext风格了。
从上图我们可以看出这个控件其实有两个视图一个日期月视图,还有一个是年月选择视图。
1:还是先从HTML入手

日期控件确定HTML其实还是比较简单,因为明摆着是列表的数据格式,当然主要是采用table了。
两个视图分别用两个Div包裹,控制div的显示隐藏即可以切换视图了。完整的HTMl结构大家可以用IEDeveloper看一下Demo的结构,我自己截了一个图
image

2:根据HTML和效果图编写CSS

其实因为是Ext风格的,所以直接copy的ext的css和图片。。

CSS也就不分析了,直接上代码。

因为博客园的语法高亮不支持CSS,所以就不贴出来了,给个下载地址吧:

 http://xuanye.cloudapp.net/Theme/Default/dp.css

所有用到的图片:

btn-arrow btn-arrow-light cal

3:搞定了CSS之后呢,就开始编写我们javascript了。

上来就是一个完整代码

那接着就是分析一下实现的主要过程和一些注意的要点:

首先还是套版化编写jQuery控件的套子:

1
2
3
4
5
;( function ($) {     
     //也可以使用$.fn.extend(datepicker:function(o){})     
     $.fn.datepicker= function (o) { 
                 }
})(jQuery);

这样做的好处上篇已经讲过了 ,就不重述了
接着就是定义默认的参数,已在代码中添加了注释说明这些参数的意义,有几个参数是为了多语言而设置的,如weekName,monthName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//给日期选择控件一个特殊的ID,获取这个ID的对象,判断如果对象存在,则直接使用
// 日期的HTML采用单例,即一个页面上只生成一份HTML
   var cp = $( "#BBIT_DP_CONTAINER" );
 
         if (cp.length == 0) {
             var cpHA = []; //老规矩还是用数组拼接html,最后用innerHTML的方式附加到容器,提升性能
             cpHA.push( "<div id='BBIT_DP_CONTAINER' class='bbit-dp' style='width:175px;z-index:999;'>" );
             if ($.browser.msie6) { //如果是IE6弹出层遮盖select
                 cpHA.push( '<iframe style="position:absolute;z-index:-1;width:100%;height:100%;top:0;left:0;scrolling:no;" frameborder="0" src="about:blank"></iframe>' );
             }
             cpHA.push( "<table class='dp-maintable' cellspacing='0' cellpadding='0' style='width:175px;'><tbody><tr><td>" );
             //头哟
             cpHA.push( "<table class='bbit-dp-top' cellspacing='0'><tr><td class='bbit-dp-top-left'> <a id='BBIT_DP_LEFTBTN' href='javascript:void(0);' title='向前一个月'>&nbsp;</a></td><td class='bbit-dp-top-center' align='center'><em><button id='BBIT_DP_YMBTN'>九月 2009</button></em></td><td class='bbit-dp-top-right'><a id='BBIT_DP_RIGHTBTN' href='javascript:void(0);' title='向后一个月'>&nbsp;</a></td></tr></table>" );
             cpHA.push( "</td></tr>" );
             cpHA.push( "<tr><td>" );
             //周
             cpHA.push( "<table id='BBIT_DP_INNER' class='bbit-dp-inner' cellspacing='0'><thead><tr>" );
             //生成周
             for ( var i = def.weekStart, j = 0; j < 7; j++) {
                 cpHA.push( "<th><span>" , def.weekName[i], "</span></th>" );
                 if (i == 6) { i = 0; } else { i++; }
             }
         
             ..... //省略若干代码
             cpHA.push( "</tbody></table>" );
             cpHA.push( "</div>" );
             cpHA.push( "</div>" );
 
             var s = cpHA.join( "" );
             $(document.body).append(s); //添加到body中
             cp = $( "#BBIT_DP_CONTAINER" ); //再获取一遍
 
             initevents(); //初始化事件
         }

这里有一个关键点,就是日期的html输出和事件初始化只做一次,因为基本上一页上同时不会打开两个。还有就是生成html中有一些特殊的自定义属性哦,仔细看下就会发现的,这些属性在后面的时间处理中都有很大的作用。那么来看一下事件吧

1
2
3
4
5
6
7
8
9
10
11
$( "#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的时候来附加事件,那么就由影响性能,所以直接给容器加了点击事件,通过对事件源的判断来分发事件,另外一个年月选择视图,也是和上面一样的逻辑,那么我们就拿月视图的点击事件来分析一下,其实每一个td生成的时候都会注册一个xdate自定义属性 image ,来看一下tbhandler函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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添加点击事件了

1
2
3
4
5
6
7
8
9
10
11
12
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的点击事件比较长,单独拿出来讲一下我想比较好,第一个要点是显示隐藏事件的处理,第二个是窗口边缘问题的处理,还有一个就是日期范围规则的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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 < 0) {
                     newpos.left = 10;
                 }
                 if (newpos.top < 0) {
                     newpos.top = 10;
                 }
                 //强制默认是月日期视图
                 $( "#BBIT-DP-MP" ).hide();
                 newpos.visibility = "visible" ;
                 cp.css(newpos); //移动到对应位置并显示
 
                  $( 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/

位置是:imageimage 

 

是datepicker在我的创造中的应用,最后如果你觉得这边文章对你有所帮助,那就点击一下【推荐】

本文的地址:http://www.cnblogs.com/xuanye/archive/2009/10/27/1590992.html

转载请保留上面的链接,谢谢

出处:http://www.cnblogs.com/xuanye/archive/2009/10/27/1590992.html

转载于:https://www.cnblogs.com/mq0036/p/8482526.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值