2007-12-24更新:
1、完全兼容IE6+、FF1.5+、opera9+。
2、添加键盘监听,支持键盘左右箭头对日期的选择。
3、除去日期显示(多余),合并周次和星期。
===========================
今天中午,老板找俺说:能不能做个小组件,我过去看了,原来是公司的工作日志系统需要一个日期控件,但是要求又比较特殊,具体要求就是:
1、日期表格横向占满页面。
2、每个月的日期列表横向显示在一排,而不是像网上的很多日期控件显示一个方块。
3、要求只有年月日可选,选择年或月后自动更新对应日期(这个每个日期控件都有的)。
4、默认显示当前年月,高亮当前日期,并显示当前的周次(本年第几周)和星期几。
5、选择某个日期后高亮显示当前日期,并自动更新周次和星期显示。
6、提供接口,可以设定特定日期的显示样式。
6、其他就是一些界面展示的问题了。
想着就是一个日期控件,做着也比较简单,不过比较特别是横向的,第一次听说这种需求的!
偶还是第一次写日历类的东西,不过这次的麻烦还是在于周次的计算上和最后提供的设定特定日期的接口实现上,但经过一番分析也很好的解决了。
演示地址:http://ajaxbbs.net/test/calendar.html主要几个总结:1、使用了闭包,隐藏内部函数及变量,防止变量污染。最后只提供一个对外接口:setDateStyle
2、计算每年二月的天数不是通过判断闰年的办法,而是通过判断是否2月29日存在,如果不存在则为28天。
3、计算周次要先计算当前日期是本年的第几天,同时要考虑本年1月1日是星期几,然后计算即可得出。
4、setDateStyle支持单个日期样式的传入,也支持多个日期样式的设定。对于样式的更新主要采用数组合并字符串,并采用字符串的indexOf方法匹配并执行样式的设定。
5、CSS/JS/HTML相分离,便于维护。函数模块化便于重用。
主要源码如下:
//使用闭包隐藏所有变量和函数,防止与外界冲突
var logDateControl=(function(){
var curSelEl; //当前选中的日期
var styleData=[],dataStyle={};
//获取指定id的元素
var $=function(id){return document.getElementById(id)}
//判断浏览器类型
var isIE=isIE||(function(){
var browser=function(str){return navigator.userAgent.indexOf(str)>-1}
return browser("MSIE")&&browser("compatible")&&!browser("Opera");
})();
//对于非IE浏览器的处理
if(!isIE){
//innerText
HTMLElement.prototype.__defineSetter__("innerText",function(sText){this.textContent=sText});
HTMLElement.prototype.__defineGetter__("innerText",function(){return this.textContent})
}
//触发click事件
var doClickEvent=function(obj){
obj=obj||window;
if(isIE){return obj.click()}
var e=document.createEvent("MouseEvents");
e.initEvent("click",true,true);
obj.dispatchEvent(e);
}
//绑定事件
var attachEvent=function(evt,handler,obj){
obj=obj||window;
if(obj.addEventListener){
obj.addEventListener(evt,handler,false);
}else{
obj.attachEvent("on"+evt,handler);
}
}
//计算指定日期是第几周(默认为当前日期),该计算方法比较严密准确
var calWeek=function(dt){
var calDay=dt||new Date(); //当前要计算的时间
var firstDay=new Date(calDay.getFullYear(),0,1); //本年第一天
//计算当前是本年的第几天,00:00为当天开始
var daysAll=Math.floor((calDay-firstDay)/1000/60/60/24)+1;
//本年第一天星期几
var firstDayWeekday=firstDay.getDay();
//该结果加到第一周的周一,便于后面计算
var diffDay=firstDayWeekday==0?6:firstDayWeekday-1;
daysAll=daysAll+diffDay;
return Math.ceil(daysAll/7); //返回计算结果
}
//计算一个月多少天,年份4位数字,月份1-2位数字(应该是js日期格式如1月传入0),数据非法返回-1
var getDaysLen=function(year,month){
if(!(/^\d{4}$/.test(year)&&/^\d{1,2}$/.test(month))){return -1}
var monthDays=[31,28,31,30,31,30,31,31,30,31,30,31]
//存在2月29日
if(month==1&&new Date(year,1,29).getMonth()==1){monthDays[1]=29}
return monthDays[month]
}
//显示日期列表,传入年、月(按日常月份传入。如二月传入2)、及显示位置
var displayDayList=function(year,month,pos){
var daysList=[];
var cells1=$(pos).rows[0].cells;
var cells2=$(pos).rows[1].cells;
var daysArr=['日','一','二','三','四','五','六'];
//下面的month-1转换为js月份表示
for(var i=1,l=getDaysLen(year,--month)+1;i<l;i++){
var wd=new Date(year,month,i).getDay();
cells1[i-1].className="";
if(wd==0||wd==6){cells1[i-1].className="weekEnd";} //为周末添加特殊样式
//_oldCls保存当前日期的默认样式
cells1[i-1].innerText=daysArr[wd];
cells2[i-1].className="unSelectDay";
cells2[i-1].setAttribute("_oldCls","unSelectDay");
cells2[i-1].innerText=i>9?i:"0"+i;
//匹配用户自定义样式
var dtStr=year+"|"+(month+1)+"|"+i;
if((","+styleData.join(',')+",").indexOf(","+dtStr+",")>-1){
cells2[i-1].className="unSelectDay "+dataStyle[dtStr];
cells2[i-1].setAttribute("_oldCls","unSelectDay "+dataStyle[dtStr]);
}
}
//如果是当前月则选中当日
if(new Date().getMonth()==month){
curSelEl=cells2[new Date().getDate()-1];
curSelEl.className="selectDay";
}
for(var j=i-1;j<31;j++){
cells1[j].className=cells2[j].className="";
cells1[j].innerHTML=cells2[j].innerHTML=" ";
}
$(pos).onclick=function(e){e=e||event;changeInfo(e)}
}
//根据选择的值进行周次和周几的调整,函数根据点击位置判断
var changeInfo=function(e){
var el=e.target||e.srcElement; //最后一个e:可能是传入的对象
var day=el.innerText;
if(!/^\d{1,2}$/.test(day)) return; //如果不是日期什么都不做
//恢复之前选中日期的样式
if(curSelEl){curSelEl.className=curSelEl.getAttribute("_oldCls")}
curSelEl=el; //保存当前处理的元素
//更新选中日期的样式
el.className="selectDay";
var dt=new Date($("year").value,$("month").value-1,day);
//更新信息
//$("day").value=day; //日期
$("weekday").value=['日','一','二','三','四','五','六'][dt.getDay()]; //星期几
$("week").value=calWeek(dt); //第几周
}
//键盘事件监听
var listenKey=function(e){
var keyCode=(e||event).keyCode;
var p=curSelEl.parentNode.cells;
var cellIndex;
if(keyCode=="37"){ //left
cellIndex=curSelEl.cellIndex!=0?curSelEl.cellIndex:p.length;
doClickEvent(p[cellIndex-1])
}
if(keyCode=="39"){ //right
cellIndex=curSelEl.cellIndex!=(p.length-1)?curSelEl.cellIndex:-1;
doClickEvent(p[cellIndex+1])
}
}
//初始化函数
var init=function(){
var curDate=new Date(),curYear=curDate.getFullYear();
//显示上下十年
for(var i=-10;i<10;i++){$("year")[$("year").length]=new Option(curYear+i,curYear+i)}
$("year").selectedIndex=10; //默认选中当前年份
$("month").selectedIndex=curDate.getMonth(); //当前月份
//$("day").value=curDate.getDate(); //当前日期
$("weekday").value=['日','一','二','三','四','五','六'][curDate.getDay()]; //当前星期几
$("week").value=calWeek(); //当前第几周
//改变日期或年份更新日期列表
$("year").onchange=$("month").onchange=function(){displayDayList($("year").value,$("month").value,"daysList")};
//显示当月日期列表,并高亮当天的日期
displayDayList(curDate.getFullYear(),curDate.getMonth()+1,"daysList");
attachEvent("keydown",listenKey,document); //添加监听keydown键盘事件
};
//初始化
attachEvent("load",init);
//对外设定样式的接口。
//格式:([2007,10,12],"color:#f00") ([[2007,10,20],[2007,11,25]],"color:#00f")
//如果月份小于10不要带0
var setDateStyle=function(dateArr,style){
if(typeof dateArr!="object")return;
if(dateArr instanceof Array){
if(dateArr[0] instanceof Array){
for(var i=0;i<dateArr.length;i++) setDateStyle(dateArr[i],style);
return;
}
var dataStr=dateArr.join('|');
styleData.push(dataStr);
dataStyle[dataStr]=style;
return;
}
}
//对外接口
return {setDateStyle:setDateStyle}
})();
//测试样式设定
logDateControl.setDateStyle([[2007,12,15],[2007,11,12]],"test");