新版,漂亮简洁的jQuery日历控件
时间:2011-04-15 来源:元某人
上一篇日志写的日历控件,后来想想还不如直接用三个select方便,而且也不漂亮。根据“最少点击”原则,后来又折腾了一段时间,做了个新的日历控件。它看上去是这样的:
点击日期,弹出选择栏,带半透明效果,滑过变色效果。
下面的确认,取消按钮是直接做在背景图上的,没有任何按钮效果(因为点两个的效果都是关闭这个控件,没必要做效果)
背景图是这样的
由于透明色简单,所以打算做成png格式,无奈IE6下错误地透明了近似颜色,导致背景图残破,只好换成gif。
HTML代码,依然是典型的div+iframe格式,用三个li显示日期,另外11个li显示弹出的数字选择。两个span做确认和取消按钮。
<div class="cldate">
<iframe frameborder="0" scrolling="no"></iframe>
<ul class="cldate_num">
<li> </li><li> </li><li> </li>
</ul>
<span class="cldate_cel"></span>
<span class="cldate_ok"></span>
<ul class="cldate_sel">
<li class="cldate_up">向前</</li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li><li></li><li></li>
<li class="cldate_down">向后</li>
</ul>
</div>
代码看起来不太整齐,不过没关系,这段代码最后要用js生成。
在CSS中,ul和span都采用绝对定位,偏移量先算好即可。
调节两个span的宽高,使之刚好框住背景上的按钮。
比较关键的是对iframe这个怪物的处理,在标准浏览器中,iframe是默认透明的,但是在IE系列中默认是白色,而且用css还消不掉,加allowtransparent属性也没用。因此,这个日历在IE浏览器中看起来是这样的:
真的是很坑爹。于是用了最暴力的方法,filter属性。
.cldate iframe{
width:188px;
height:170px;
z-index:-1;
position:absolute;
filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);
}
然后,问题解决了。
比起上一个日历控件,这个控件的JS代码部分难度要小很多,主要都是常规控制。
代码依然由三部分组成:
1.动态生成控件节点,插入DOM。绑定input框。
2.给两个UL,两个SPAN绑定点击事件,一共4个。
3.处理点击,计算日期,刷新显示。
第一部分和上一篇日志里写的大同小异,第二部分通过jQuery的链式表达式连续写下来即可。
第三部分,日期计算和显示分别独立成两个函数。其中计算函数要判断当前要处理的是年月或者日,然后判断刷新区域。对于年份,要判断是否过小或过大。对于月份,直接限制为12即可。对于日,则要根据月和年,计算出当月的天数,然后判断区域。此函数如下:
var initSel=function(type,start){ //刷新备选数字
switch(type){
case 0:
if(start<1900)
return false;
break;
case 1:
if(start<1) //1--9
start=1;
if(start>4) //4--12
start=4;
break;
case 2:
if(start<1)
start=1;
else if(start>20){ //28,29,30,31
switch(time[1]){
case 1:case 3:case 5:case 7:case 8:case 10:case 12:
if(start>23)
start=23;
break;
case 4:case 6:case 9:case 11:
if(start>22)
start=22;
break;
default: //2
if(time[0]%400==0 || time[0]%4==0){//闰年
if(start>21)
start=21;
break;
}
start=20;
break;
}
} //else if
}//switch
$item.each(function(i){this.innerHTML=start+i;});
};
传递两个参数,type表示要判断的是年/月/日,start是那个数字列表的建议起始数。“向前”对应的起始数就是数字列表里第一个数减9,“向后”对应加9。所以对于月,日,可能出现-8,40这样的值。于是在后面的程序在做个范围限制。点击数字,表示选中。
这里有个潜在问题,比如用户选了12月31日,然后将月改成11。而11月只有30天。又如用户选了2012年2月29日,然后把年改为2011年。因此,在刷新显示之前,还要在判断新的日期是否合法,也就当日期值大于28时,检查对应月份是否有那么多天。
//这里检查日期合法否1.3.5...月不需判断,他们支持最多的天数。
if(time[2]>28) {
switch(time[1]){
case 4:case 6:case 9:case 11:
if(time[2]>30)
time[2]=30;
break;
case 2: //day至少29,
if(time[0]%400==0 || time[0]%4==0)
time[2]=29;
else
time[2]=28;
break;
}
}
最后需要说的是,单按用户选中所需日期的点击次数来看,这个控件还是没有直接用三个select方便。比如选1949年10月1日,用户需要连点6次“向前”才能找到1949这个选项。
不过好处是,它在所有浏览器中风格一致,而select不一致。