[javascript]简单 方便 易折腾的日历控件,纯javascript支持链式语法,核心代码只有5行

2012-07-03 22:07

[javascript]简单 方便 易折腾的日历控件,纯javascript支持链式语法,核心代码只有5行

by 小玉西瓜

at 2012-07-03 14:07:00

original http://www.cnblogs.com/enix/archive/2012/07/03/2574663.html

又重复造了个轮子,重复造轮子当然要有优点要不就没有存在价值。使用简单,方便。只要5行代码就可生成年历。


input date已经是一个原生控件opera和chrome已经支持,日历控件逻辑比较复杂,要做到尽善尽美也是不容易的,也是比较锻炼编码的,所以学习意义大于实际意义。而且这个控件也是多年心愿,最近有时间写写停停,终于坚持下来就拿来和大家分享了。


 


 


传送门


 


特点


1.此日历插件特点在于简单易用,要保证功能实现最低要求一个参数。


2.个性化配置有强大的自定义事件和多个个性化参数可选。


3.采用原型继承,扩展派生非常方便。


4.跨浏览器,保证可用性。


5.采用表格做载体,无样式也可以正常使用功能。


6.缓存,已生成月份数据。使数据持久化,减少计算。


7.无样式支持,功能依然健在。



应用展示


a.



b.



c.




 


技术总结


1.主要难点不是日期算法,而是select控件和表格所带来的问题。select和表格都有一套自己的dom api,来操作和访问。尤其是生成日历table的算法非了一些时间,而且ie浏览器上。innerHTML属性,table tbody tfoot tr都是只读的,所以对技术选型有一些干扰。中间使用了insertRow这样的方法未果,采用分辨浏览器采用不同方案也行不通后,过段采用整个table都用拼字符串的方式。虽然效率不一定比生成dom节点存入变量修改高,但是结果却是最好的。


2.select主要问题在于序号和长度差,要访问selectedIndex,设置select的值都要小心这个变化。


3.Date对象的序号问题,年 日都是从1开始,月份是从0开始,这些差别和人类习惯的处理带来一定成本。


可见初期,技术储备和技术选型的重要性。


其实日期核心算法只有5行代码而已,想要生成一个可用的日期控件20行内代码足够,由于和日期控件相关的控件出来带来了大量代码。


 


核心代码


 


1。计算某个月有多少天,2012.6月到底有多少天呢?我们要是知道6月最后一天是多少号,不就知道6月有多少天了吗。



new Date('2012,6,0').getDate();最简单最短 //chrome不支持用字符串new Date('2012,6,0')格式创建日期, 但支持new Date('2012','6','0');这点要注意 而safari没有问题非常诡异


通用的



(new Date(+(new Date(2012, 7, 1)) - 86400000)).getDate();


下个月的一号减去一天,就是当月最后一天


 


2.知道了当月有多少天还不行,要知道当月1号是要放在什么位置,如果知道1号是星期几不就可以知道1号在什么位置了吗。



new Date(2012, 7,1).getDay();


 


3.生成一个月的日历,代码很简单只有5行



for (var i = 0; i < row; i++) {
  html.push('</tr>');
for (var j = 0; j < 7; j++) {
html.push((cell-- <= offset && days > 0) ? ('<td ' + (that.Date > new Date(y, m, days + 1) ? 'class="oldDay"' : '') + (y === that.Year && m === that.Month && days === that.Day ? 'class="currentDay"' : '') + ' title="'+y+that.style+(m+1)+that.style+days+'">' + (days--) + '</td>') : '<td></td>');
}
html.push('<tr>');
}


大家可能要怀疑为什么先写的是 </tr>结束标签,开始生成日历的时候采用的是倒序循环,用li标签,后面发现无样式时不能使用,所以改用table但这一方式保留下来,日期也是按照从后往前生成的。然后调用数组的reverse反向join就正序了。


 


4.一个实例的6月份数据和另外一个实例的6月份数据有不同点么,或者重复使用6月份数据需要每次都计算么,答案显然不是。相同月份的数据多次使用要是只计算一次的话就可以大大提高效率,减少计算次数。


那么这就牵扯到数据持久化的问题,怎样把数据持久化呢或者暂时持久化?答案是把数据保存在类的属性上随用随拿。


 



var key = 'date:' + Y + ':' + M;
indicotar.cache[key] || (indicotar.cache[key] = that.getDateString(Y, M));


按照年月生成一个key,如果没有数据就调用方法生成数据存入对应key的value。以后只用只用取出,合并字符串即可。



 


 



API


constructor:Cal(可以自行修改无依赖);

实例化:new Cal(document.body);
返回值:实例对象;















参数

Node
*htmlNode
nodeType为1的节点,控件会被append此节点内











Object
O
配置控件可选参数











Number
O.Y
设置的年份,默认当年,范围(1970-当年+10)











Number
O.M
设置的月份,默认当月,范围(1-12)











String
O.hasTitle
是否有日期控制栏,默认有'true'











String
O.hasFoot
是否有脚注用来显示年月,默认无'false'











String
O.style
日期分隔符,默认'-'











Number
O.startYear
开始年,默认2006











Number
O.endtYear
结束年,默认当年加10











Function
O.ongetdate
用户点击日期单元格时触发,this指向实例,第一个参数为日期对应数组











Function
O.onrender
控件插入值dom树时触发,this指向实例,第一个参数为控件对应的dom节点











Function
O.ongetdatestring
获得月份所对应的日期字符串时触发,this指向实例,第一个参数为字符串











Function
O.oncalframe
组成完毕控件node框架时触发,this指向实例,第一个参数为对应的dom节点











Function
fn
类的回调函数,生成控件后触发
































 












实例方法

方法名
参数
返回值











createDay
Y:number/string(2012),M:number/string(0-11)
实例(1储存日期字符串至Cal的cache中,是数据持久化;2调用render)











render
node:(this.elems),key:string('date:2012:0'),Y:number(2012),M:number(0-11)
实例(渲染控件至dom树)











getDateString
Y:number(2012),M:number(0-11)
htmlString











toString
 
string '2012-12-12' (获取选中日期对应的字符串)











valueOf
 
Array [2012,12,12] (获取选中日期对应的数组)











hide
 
实例 (隐藏控件)











show
 
实例 (显示控件)











setCss
Object({'font-size':'12px','width':'300px'})
实例 (为控件添加样式)










































本文链接