JCalendar.js

1 //////////////////基本函数库///////////////////////////// 2 var isIE = /msie/i.test(navigator.userAgent); 3 var isDTD = /CSS1Compat/i.test(document.compatMode); 4 if(!isIE){ 5 window.constructor.prototype.__defineGetter__("event",function(){ 6 var func = arguments.callee.caller; 7 while(func != null){ 8 var arg0 = func.arguments[0]; 9 if(arg0 && (arg0.constructor==Event || arg0.constructor ==MouseEvent)){ 10 return arg0; 11 } 12 func = func.caller; 13 } 14 return null; 15 }); 16 Event.prototype.__defineSetter__("returnValue",function(b){ 17 if(!b)this.preventDefault(); 18 return b; 19 }); 20 Event.prototype.__defineGetter__("srcElement",function(){ 21 var node=this.target; 22 while(node.nodeType != 1)node=node.parentNode; 23 return node; 24 }); 25 Event.prototype.__defineGetter__("offsetX",function(){ 26 return this.layerX; 27 }); 28 Event.prototype.__defineGetter__("offsetY",function(){ 29 return this.layerY; 30 }); 31 HTMLElement.prototype.attachEvent = function(sType,foo){ 32 this.addEventListener(sType.slice(2),foo,false); 33 } 34 HTMLElement.prototype.detachEvent = function(sType,foo){ 35 this.removeEventListener(sType.slice(2),foo,false); 36 } 37 HTMLDocument.prototype.attachEvent = function(sType,foo){ 38 this.addEventListener(sType.slice(2),foo,false); 39 } 40 HTMLDocument.prototype.detachEvent = function(sType,foo){ 41 this.removeEventListener(sType.slice(2),foo,false); 42 } 43 HTMLElement.prototype.__defineGetter__("innerText",function(){ 44 return this.textContent; 45 }); 46 HTMLElement.prototype.__defineSetter__("innerText",function(str){ 47 this.textContent = str; 48 }); 49 } 50 else document.execCommand("BackgroundImageCache",false,true); 51 function $(id){return (typeof id == "string" ? document.getElementById(id) : id);} 52 function $N(name){return document.getElementsByName(name);} 53 function $TN(name,root){return root ? $(root).getElementsByTagName(name) : document.getElementsByTagName(name);} 54 function $DC(name){return document.createElement(name);} 55 function exist(id){return $(id)!= null;} 56 function hide(){ 57 for(var i = 0; i < arguments.length; i++){ 58 if(exist(arguments[i])){ 59 if($(arguments[i]).style.visibility) $(arguments[i]).style.visibility = "hidden"; 60 else $(arguments[i]).style.display = "none"; 61 } 62 } 63 } 64 function show(){ 65 for(var i = 0; i < arguments.length; i++){ 66 if(exist(arguments[i])){ 67 if($(arguments[i]).style.visibility) $(arguments[i]).style.visibility="visible"; 68 else $(arguments[i]).style.display = ""; 69 } 70 } 71 } 72 //////////////JCalendar 类//////////////////////////// 73 /*************************** 74 *JCalendar日历控件 75 *@author brull 76 *@email [email]brull@163.com[/email] 77 *@date 2007-4-16 78 *@更新 2007-5-27 79 *@version 1.0 beta 80 ***************************/ 81 /* 82 *@param year 年份,[可选] 83 *@param month 月份,[可选] 84 *@param date 日期,[可选] 85 *或者是以横线间隔开的日期,比如:2007-4-27 86 */ 87 88 function JCalendar (year,month,date) { 89 if($("calendar"))return;//唯一实例 90 var _date = null; 91 if(arguments.length == 3) _date = new Date(year,month-1,date); 92 else if(arguments.length == 1 && typeof arguments[0] == "string") 93 _date = eval("new Date(" + arguments[0].split("-").join() + ")"); 94 //如果没有参数,就初始化为当天日期 95 else if(arguments.length == 0) _date = new Date(); 96 this.year = _date.getFullYear(); 97 this.month = _date.getMonth() + 1; 98 this.date = _date.getDate(); 99 this.FIRSTYEAR = 1949; 100 this.LASTYEAR = 2049; 101 JCalendar.cur_year = this.year; 102 JCalendar.cur_month = this.month; 103 JCalendar.cur_date = this.date; 104 JCalendar.cur_obj_id = null;//作为输入控件时保存当前文本框的id 105 } 106 /** 107 *设置日历年份下拉菜单的年份范围 108 *@first 第一个年份界限 109 *@last 第二个年份界限 110 *两个参数顺序可以颠倒 111 */ 112 JCalendar.prototype.setYears = function(first,last){ 113 if(isNaN(first) || isNaN(last)) return; 114 this.FIRSTYEAR = Math.min(first,last); 115 this.LASTYEAR = Math.max(first,last); 116 } 117 /** 118 *以HTML串返回日历控件的HTML代码 119 */ 120 JCalendar.prototype.toString = function(){ 121 var fday = new Date(this.year,this.month-1,1).getDay();//每月第一天的星期数 122 var select_year = new Array();//年份下拉菜单 123 var select_month = new Array();//月份下拉菜单 124 //日历里的每个单元格的数据,预先定义一段空数组,对应日历里第一周空的位置。[注意星期天对应的数是0] 125 var date = new Array(fday > 0 ? fday - 1 : 0); 126 var dayNum = new Date(this.year,this.month,0).getDate();//每月的天数 127 var html_str = new Array();//保存日历控件的HTML代码 128 var date_index = 0;//date数组的索引 129 var weekDay = ["日","一","二","三","四","五","六"]; 130 131 //填充年份下拉菜单 132 select_year.push("<select id='select_year' style='display:none' onblur ="hide(this);show('title_year')" onchange='JCalendar.update(this.value,JCalendar.cur_month)'>"); 133 for(var i = this.FIRSTYEAR; i <= this.LASTYEAR; i++){ 134 if(i == this.year) 135 select_year.push("<option value='" + i + "' selected='selected'>" + i +"</option>"); 136 else 137 select_year.push("<option value='" + i + "'>" + i +"</option>"); 138 } 139 select_year.push("</select>"); 140 141 //填充月份下拉菜单 142 select_month.push("<select id='select_month' style='display:none' onblur ="hide(this);show('title_month')" onchange='JCalendar.update(JCalendar.cur_year,this.value)'>"); 143 for(var i = 1; i <= 12; i++){ 144 if(i == this.month) 145 select_month.push("<option value='" + i + "' selected='selected'>" + i +"月</option>"); 146 else 147 select_month.push("<option value='" + i + "'>" + i +"月</option>"); 148 } 149 select_month.push("</select>"); 150 //初始化date数组 151 for(var j = 1; j <= dayNum; j++){ 152 date.push(j); 153 } 154 //开始构建日历控件的HTML代码 155 html_str.push("<table id='calendar'>"); 156 //日历表格caption 157 html_str.push("<caption>" + "<a href='#' id='prev_month' title='上一月份' onclick="JCalendar.update(JCalendar.cur_year,JCalendar.cur_month-1);return false;">«</a><a href='#' id='title_year' title='点击选择年份' onclick="hide(this);show('select_year');$('select_year').focus();return false">" + this.year + "年</a>" + select_year.join("") + "<a href='#' id='title_month' title='点击选择月份' onclick="hide(this);show('select_month');$('select_month').focus();return false">" + this.month + "月</a>" + select_month.join("") + "<a href='#' id='next_month' title='下一月份' onclick="JCalendar.update(JCalendar.cur_year,JCalendar.cur_month+1);return false;">»</a></caption>"); 158 //日历表格头 159 html_str.push("<thead><tr>"); 160 for(var i = 0; i < 7; i++){//填充日历头 161 html_str.push("<td>" + weekDay[i] + "</td>"); 162 } 163 html_str.push("</tr></thead>"); 164 //日历主体 165 var tmp; 166 html_str.push("<tbody>"); 167 for(var i = 0; i < 6; i++){//填充日期,6行7列 168 html_str.push("<tr>"); 169 for(var j = 0; j < 7; j++){ 170 tmp = date[date_index++]; 171 if(!tmp) tmp = ""; 172 html_str.push("<td "); 173 if(tmp == this.date) html_str.push("id='c_today' "); 174 html_str.push("onmouseover='JCalendar.over(this)' onmouseout='JCalendar.out(this)' onclick='JCalendar.click(this)'>" + tmp + "</td>"); 175 } 176 html_str.push("</tr>"); 177 } 178 html_str.push("</tbody></table>"); 179 return html_str.join(""); 180 } 181 /** 182 *特别显示关键天,典型例子博客的日历 183 * 实现原理,为每个关键天的表格单元添加一个class,名字为keydate,CSS样式需要自己写,比如加个背景之类的 184 *@param 日期的数组,比如:[1,4,6,9] 185 */ 186 JCalendar.prototype.setKeyDate = function(){ 187 var dates = arguments[0]; 188 var tds = $TN("td",$("calendar")); 189 var reg = null; 190 for(var i = 0; i < dates.length; i++){ 191 reg = new RegExp("\b" + dates[i] + "\b"); 192 for(var j = 7; j < tds.length; j++){//忽略表格头 193 if(reg.test(tds[j].innerText)){ 194 tds[j].className = "keydate"; 195 break; 196 } 197 } 198 } 199 } 200 /** 201 *可以将日历控件邦定到某个文本框,在点击文本框的时候,会在direction指定的方向弹出日历,可以多次调用来帮定多个文本框 202 *@ param obj_id 需要邦定日历的文本框的id 203 *@ param direction 日历出现的相对于文本框的方向 [可选] 默认为right 204 */ 205 JCalendar.prototype.bind = function(obj_id,direction){ 206 var obj = $(obj_id); 207 var direction = direction ? direction : "right"; 208 if(!obj)return; 209 if(!$("calendar_container")){//唯一容器 210 var contain = $DC("div"); 211 var s = contain.style; 212 s.visibility = "hidden"; 213 s.position = "absolute"; 214 s.top = "0px";//不能占据页面空间 215 s.zIndex = 65530; 216 contain.id = "calendar_container"; 217 contain.innerHTML = this.toString(); 218 document.body.appendChild(contain); 219 if(isIE){ 220 var ifm = $DC("iframe"); 221 var s = ifm.style; 222 ifm.frameBorder = 0; 223 ifm.height = (contain.clientHeight - 3) + "px"; 224 s.visibility = "inherit"; 225 s.filter = "alpha(opacity=0)"; 226 s.position = "absolute"; 227 s.top = "0px";//不能占据页面空间 228 s.width = $("calendar_container").offsetWidth; 229 s.zIndex = -1; 230 contain.insertAdjacentElement("afterBegin",ifm); 231 } 232 } 233 //覆盖日历事件 234 JCalendar.onupdate = function () {}; 235 JCalendar.onclick = function (year,month,date){ 236 $(JCalendar.cur_obj_id).value = $(JCalendar.cur_obj_id).value.replace(/^[^s]*/i,year + '-' + month + '-' + date); 237 hide("calendar_container"); 238 } 239 //邦定事件 240 document.attachEvent("onclick",function(){ 241 if($("calendar_container").style.visibility="visible")hide("calendar_container"); 242 }); 243 obj.attachEvent("onclick",function(e){ 244 var obj = e.srcElement; 245 var dates =obj.value.split(/s/)[0].split("-");//文本框日期数组,文本框内容可能有时间这样的字串,即:2007-5-26 15:39 246 var left = obj.offsetLeft , top = obj.offsetTop; 247 var x,y,left,top; 248 var contain = $("calendar_container"); 249 var body = isDTD ? document.documentElement : document.body; 250 left = body.scrollLeft + e.clientX - e.offsetX; 251 top = body.scrollTop + e.clientY - e.offsetY; 252 switch(direction){ 253 case "right" : x = left + obj.offsetWidth; y = top;break; 254 case "bottom" : x = left; y = top + obj.offsetHeight;break; 255 } 256 contain.style.top = y + "px"; 257 contain.style.left = x + "px"; 258 //更新日历日期 259 if(dates.length == 3 && (JCalendar.cur_year != dates[0] || JCalendar.cur_month != dates[1] || JCalendar.cur_date != dates[2])) 260 JCalendar.update(dates[0],dates[1],dates[2]);//如果文本框有时间则更新时间到文本框的时间 261 else if (dates.length != 3){ 262 var now = new Date(); 263 JCalendar.update(now.getFullYear(),now.getMonth() + 1,now.getDate()); 264 } 265 if($("calendar_container").style.visibility="hidden")show("calendar_container"); 266 e.cancelBubble = true; 267 JCalendar.cur_obj_id = obj.id; 268 }); 269 $("calendar_container").attachEvent("onclick",function(e){e.cancelBubble = true;}); 270 } 271 /*===========================静态方法=======================================*/ 272 /** 273 *更新日历内容 274 */ 275 JCalendar.update = function(_year,_month,_date){ 276 date = new Date(_year,_month-1,1); 277 var fday = date.getDay();//每月第一天的星期数 278 var year = date.getFullYear(); 279 var month = date.getMonth() + 1; 280 var dayNum = new Date(_year,_month,0).getDate();//每月的天数 281 var tds = $TN("td",$("calendar")); 282 var years = $("select_year").options; 283 var months = $("select_month").options; 284 var _date = _date ? _date : JCalendar.cur_date; 285 //更新当前年月 286 JCalendar.cur_year = year; 287 JCalendar.cur_month = month; 288 if(_date && _date <= dayNum) JCalendar.cur_date = _date; 289 else if(_date > dayNum) JCalendar.cur_date = _date - dayNum; 290 $("title_year").innerText = year + "年"; 291 $("title_month").innerText = month + "月"; 292 //更新年份下拉菜单选中项 293 for(var i = years.length - 1; i >= 0; i-- ){ 294 if(years[i].value == year){ 295 $("select_year").selectedIndex = i; 296 break; 297 } 298 } 299 //更新月份下拉菜单选中项 300 for(var i = months.length - 1; i >= 0; i-- ){ 301 if(months[i].value == month){ 302 $("select_month").selectedIndex = i; 303 break; 304 } 305 } 306 //清空日历内容,忽略日历头,即第一行 307 for(var i = 7; i < tds.length; i++) tds[i].innerText = ""; 308 if( $("c_today"))$("c_today").removeAttribute("id"); 309 for(var j = 1; j <= dayNum; j++){ 310 tds[6 + fday + j].innerText = j; 311 if(j == JCalendar.cur_date) tds[6 + fday + j].id = "c_today"; 312 } 313 JCalendar.onupdate(year,month,JCalendar.cur_date); 314 } 315 JCalendar.click = function(obj){ 316 var tmp = $("c_today"); 317 if(tmp && tmp == obj){ 318 JCalendar.onclick(JCalendar.cur_year,JCalendar.cur_month,JCalendar.cur_date); 319 } 320 else if(obj.innerText != ""){ 321 if(tmp) tmp.removeAttribute("id"); 322 JCalendar.cur_date = parseInt(obj.innerText); 323 obj.id = "c_today"; 324 JCalendar.onclick(JCalendar.cur_year,JCalendar.cur_month,JCalendar.cur_date); 325 } 326 } 327 JCalendar.over = function(obj){ 328 if(obj.innerText != "") obj.className = "over"; 329 } 330 JCalendar.out = function(obj){ 331 if(obj.innerText != "") obj.className = ""; 332 } 333 //日历更改时执行的函数,可以更改为自己需要函数,控件传递过来的参数为当前日期 334 JCalendar.onupdate = function(year,month,date){ 335 alert("日历已更改,当前日历日期:" + year + "年" + month + "月" + date + "日"); 336 } 337 //点击日期时执行的函数,可以更改为自己需要函数,控件传递过来的参数为当前日期 338 JCalendar.onclick = function(year,month,date){ 339 alert( "当前触发的日期:" + year + "年" + month + "月" + date + "日"); 340 } 341 function showCalendar (obj) 342 { 343 var j = new JCalendar(); 344 j.setYears(1990, 2020); 345 j.bind(obj, "bottom"); 346 //j.bind('b'); 347 }
Calendar.css

1 /**********日历样式开始********************/ 2 #calendar_container { 3 width:160px; 4 border:1px solid #06C; 5 } 6 #calendar { 7 border-collapse:collapse; 8 background-color:#FFF; 9 width:160px; 10 height:120px; 11 margin:0px auto; 12 cursor:default; 13 } 14 #calendar td { 15 font-size:12px; 16 text-align:center; 17 vertical-align:middle; 18 font-family:"宋体"; 19 } 20 #calendar thead { 21 background-color:#999; 22 color:#FFF; 23 } 24 #calendar caption { 25 background-color:#06C; 26 } 27 #calendar a{ 28 color:#F90; 29 margin:0 5px; 30 text-decoration:none; 31 } 32 #calendar #prev_month,#calendar #next_month { 33 font-size:18px; 34 margin:0; 35 } 36 #calendar #c_today { 37 background-color:#036; 38 color:#FFF; 39 } 40 #calendar .over { 41 background-color:#CCC; 42 } 43 #calendar .keydate { 44 color:#06F; 45 }
页面调用:
<link href="calendar.css" type="text/css" rel="stylesheet" /> <script language="javascript" src="JCalendar.js" type="text/javascript"> </script> <input type="text" onclick='showCalendar(this)' />