I've seen my share of Dynamic HTML Date Pickers, Javascript Calendar controls, and the like. Each of which have a more horrifically ugly interface and are bloated with features. I found a need to create a simple calender piece so that a user could add a product to their cart when clicking on a specific date.
The example below shows two instantiated calendar's each automatically showing the current month as well as blocking out the days before the current day in the previous month and blocking out days after the current day in the next month.
Update 10/31/2007 - v0.4
Fixed 2nd month displayed incorrectly - pointed out by K. Ng
Fixed another flaw with the trailing month days displayed in every month. V 0.3 and earlier you could see this problem on May 2008.
Update 9/24/2007 - v0.3
now click+release on a single day to add day
click hold and drag to select multiple days
can limit the user to the maximum number of concurrent days, i.e. 10 in this example
messaging at the bottom of the calendar to help instruct the user
Update 9/21/2007 - v0.2
display two months horizontally
left to right chronological navigation steps still one month in each direction
ability to build a "list" of selected days
first stab at showing the date range the user is about to select
date range can span multiple months
note: this code will need be cleaned and allow up to 12 months side-by-side
The javascript Calendar "class" I came up with offer these features:
a simple design that's not ugly
ability to display the current date by default
can be drawn dynamically into any content container: DIV, SPAN, TD, or any element with an W3C-specification-defying innerHTML attribute
ability to "disable" or block-out specific dates within a range
easy access to scrolling through the months forward or reverse from current date
This was minimally tested on IE 6+, FF, and Safari 2.X.
The source for instantiating the test cases of the Calendar Javascript "class":
<html>
<head>
<scriptlanguage="JavaScript"type="text/javascript"src="calendar.js"></script>
<script>
var __calendars=new Array();
__calendars.push(new Calendar(0,
'_calendar0',
new Date((new Date()).getMonth()+'/'+(new Date()).getDate()+'/'+(new Date()).getFullYear()),
new Date(((new Date()).getMonth()+2)+'/'+(new Date()).getDate()+'/'+(new Date()).getFullYear())));
__calendars.push(new Calendar(1,
'_calendar1',
new Date((new Date()).getMonth()+'/'+(new Date()).getDate()+'/'+(new Date()).getFullYear()),
new Date(((new Date()).getMonth()+2)+'/'+(new Date()).getDate()+'/'+(new Date()).getFullYear())));
function initCal(){// draw the current month's calendar
document.getElementById('calContainer1').innerHTML=__calendars[0].drawCal(0);
document.getElementById('calContainer2').innerHTML=__calendars[1].drawCal(0);
}</script>
</head>
<bodystyle="background:#ff9900;"onLoad="initCal();"topmargin=0bottommargin=0leftmargin=0rightmargin=0marginheight=0marginwidth=0>
<spanid="calContainer1">Loading...</span><spanid="calContainer2">Loading...</span>
</body>
</html>
The source for the Calendar Javascript "class":
var __monthslong=["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var __monthsshort=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
function Calendar(arrayID, uniqueName, dateFrom, dateTo){this.currDate=new Date();
this.uniqueName=uniqueName;
this.arrayID=arrayID;
this.products=new Array();
this.selectedDays=new Array();
this.prodDesc='';
this.dateFrom=dateFrom;
this.dateTo=dateTo;
this.startTally=new Array();
this.maxDays=10;
}Calendar.prototype.drawCal=function (nudgeMonth, alreadyLoaded){this.currDate.setMonth((this.currDate.getMonth()+nudgeMonth));
var tmpNextMo=new Date();
tmpNextMo.setDate(1);
tmpNextMo.setFullYear(this.currDate.getFullYear());
tmpNextMo.setMonth(parseInt(this.currDate.getMonth())+1);
/* flawed next month display above calendar -
* if next month has less days than first month,
* it'll skip the correct month */// var tmpNextMo = new Date(this.currDate.getFullYear(), (this.currDate.getMonth() + 1), this.currDate.getDate());
var _currDate=new Date();
_currDate.setFullYear(this.currDate.getFullYear());
_currDate.setMonth(this.currDate.getMonth());
var calStr='<table cellpadding=0 cellspacing=0 border=0>'+'<tr>'+'<td style="background:url(ul.gif) no-repeat; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>'+'<td style="background:url(ur.gif) right top no-repeat; height:6px; width:6px;"><img width="6" height="6" border=0 src="_blank.gif"></td>'+'</tr>'+'<tr>'+'<td style="background:url(ml.gif) left top repeat-y; text-align:center; font-family:Tahoma; font-size:8pt; color:#808080; overflow:auto; padding-left:6px;" id="__calMessage">'+'<table cellpadding=0 cellspacing=0 border=0>'+'<tr>'+'<td width="100%" colspan=2 align=middle style="padding-bottom:5px;">'+'<table cellpadding=0 cellspacing=0 border=0 width="100%">'+'<tr>'+'<td align=left style="font-size:8pt; color:#000000; text-align:left; cursor:pointer;" '+'onClick="__calendars['+this.arrayID+'].drawCal((-1), true);">'+'<img border=0 src="_left.gif">'+'</td>'+'<td width="50%" align=left style="padding:0px 16px 0px 0px; font-size:8pt; color:#000000; text-align:center; cursor:default;">'+'<nobr>'+__monthslong[this.currDate.getMonth()]+' '+this.currDate.getFullYear()+'</nobr>'+'</td>'+'<td width="50%" align=right style="padding:0px 0px 0px 16px; font-size:8pt; color:#000000; text-align:center; cursor:default;">'+'<nobr>'+__monthslong[tmpNextMo.getMonth()]+' '+tmpNextMo.getFullYear()+'</nobr>'+'</td>'+'<td align=right style="font-size:8pt; color:#000000; text-align:right; cursor:pointer;" '+'onClick="__calendars['+this.arrayID+'].drawCal(1, true);">'+'<img border=0 src="_right.gif">'+'</td>'+'</tr>'+'</table>'+'</td>'+'</tr>'+'<tr>'+'<td valign=top>'+'<table cellpadding=0 cellspacing=0 border=0>'+'<tr>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">M</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">W</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">F</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>'+'</tr>'+'<tr>';
// set to the first day
this.currDate.setDate(1);
var pMonth=new Date();
pMonth.setFullYear(this.currDate.getFullYear());
pMonth.setMonth(this.currDate.getMonth());
pMonth.setDate((this.currDate.getDate()-1));
// get current month days count
var dCount=new Date();
dCount.setDate(1);
dCount.setFullYear(this.currDate.getFullYear());
dCount.setMonth((this.currDate.getMonth()+1));
dCount.setDate(dCount.getDate()-1);
/* FLAWED
var dCount = new Date();
dCount.setFullYear(this.currDate.getFullYear());
dCount.setMonth((this.currDate.getMonth() + 1));
dCount.setDate((this.currDate.getDate() - 1));
*/var dayCounter=0;
// perform the previous months last days
for (var pMo=1; pMo<=this.currDate.getDay(); pMo++){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;">'+(pMonth.getDate()-this.currDate.getDay()+pMo)+'</td>';
dayCounter++;
}// now display all days in current month
for (var cMD=0; cMD < dCount.getDate(); cMD++){var validFrom=true;
// find if this is a valid day or not
if (typeof(this.dateFrom)!='undefined'){if (this.dateFrom.getTime() > (new Date((this.currDate.getMonth()+1)+'/'+(this.currDate.getDate()+cMD)+'/'+this.currDate.getFullYear())).getTime()){validFrom=false;
}}var validTo=true;
// find if this is a valid day or not
if (typeof(this.dateTo)!='undefined'){if (this.dateTo.getTime() < (new Date((this.currDate.getMonth()+1)+'/'+(this.currDate.getDate()+cMD)+'/'+this.currDate.getFullYear())).getTime()){validTo=false;
}}var dayInList=false;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==this.currDate.getFullYear()&&this.selectedDays[dInd][1]==this.currDate.getMonth()&&this.selectedDays[dInd][2]==(this.currDate.getDate()+cMD)){dayInList=true;
}}if (validFrom==false ||validTo==false){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; background:#EFEFEF; font-size:8pt; color:#808080; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;';
}else {calStr+='<td id="__cal'+this.arrayID+'__'+this.currDate.getFullYear()+'-'+this.currDate.getMonth()+'-'+(this.currDate.getDate()+cMD)+'" '+'align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:pointer;';
if (dayInList){calStr+=' background:#006699; color:#FFFFFF;';
}else {calStr+=' background:#FFFFFF; color:#808080;';
}}if (validFrom==false ||validTo==false){calStr+=' text-decoration:line-through;';
}if (validFrom==true&&validTo==true){calStr+='" '+'onMouseOver="__calendars['+this.arrayID+'].toggleDayHilight(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, true);" ';
}if (validFrom==true&&validTo==true){// 'onClick="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' +
calStr+='" '+'onMouseOut="__calendars['+this.arrayID+'].toggleDayHilight(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, false);" '+'onMouseDown="__calendars['+this.arrayID+'].toggleDay(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, \'down\');" '+'onMouseUp="__calendars['+this.arrayID+'].toggleDay(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, \'up\');">'+(this.currDate.getDate()+cMD)+'</td>';
}else {calStr+='">'+(this.currDate.getDate()+cMD)+'</td>';
}dayCounter++;
if ((dayCounter%7)==0){calStr+='</tr>';
if (cMD!=dCount.getDate()){'<tr>';
}}}// display the tail end of the month
for (var eMo=1; eMo<=(6-dCount.getDay()); eMo++){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #FFFFFF; text-align:center; cursor:default;">'+eMo+'</td>';
dayCounter++;
}calStr+='</table>'+'</td>'+'<td valign=top style="padding-left:5px;">'+'<table cellpadding=0 cellspacing=0 border=0>'+'<tr>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">M</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">W</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">F</td>'+'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>'+'</tr>'+'<tr>';
// set to the first day
this.currDate.setMonth((this.currDate.getMonth()+1));
this.currDate.setDate(1);
var pMonth=new Date();
pMonth.setFullYear(this.currDate.getFullYear());
pMonth.setMonth(this.currDate.getMonth());
pMonth.setDate((this.currDate.getDate()-1));
// get current month days count
var dCount=new Date();
dCount.setDate(1);
dCount.setFullYear(this.currDate.getFullYear());
dCount.setMonth((this.currDate.getMonth()+1));
dCount.setDate(dCount.getDate()-1);
/* FLAWED
var dCount = new Date();
dCount.setFullYear(this.currDate.getFullYear());
dCount.setMonth((this.currDate.getMonth() + 1));
dCount.setDate((this.currDate.getDate() - 1));
*/var dayCounter=0;
// perform the previous months last days
for (var pMo=1; pMo<=this.currDate.getDay(); pMo++){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;">'+(pMonth.getDate()-this.currDate.getDay()+pMo)+'</td>';
dayCounter++;
}// now display all days in current month
for (var cMD=0; cMD < dCount.getDate(); cMD++){var validFrom=true;
// find if this is a valid day or not
if (typeof(this.dateFrom)!='undefined'){if (this.dateFrom.getTime() > (new Date((this.currDate.getMonth()+1)+'/'+(this.currDate.getDate()+cMD)+'/'+this.currDate.getFullYear())).getTime()){validFrom=false;
}}var validTo=true;
// find if this is a valid day or not
if (typeof(this.dateTo)!='undefined'){if (this.dateTo.getTime() < (new Date((this.currDate.getMonth()+1)+'/'+(this.currDate.getDate()+cMD)+'/'+this.currDate.getFullYear())).getTime()){validTo=false;
}}var dayInList=false;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==this.currDate.getFullYear()&&this.selectedDays[dInd][1]==this.currDate.getMonth()&&this.selectedDays[dInd][2]==(this.currDate.getDate()+cMD)){dayInList=true;
}}if (validFrom==false ||validTo==false){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; background:#EFEFEF; font-size:8pt; color:#808080; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;';
}else {calStr+='<td id="__cal'+this.arrayID+'__'+this.currDate.getFullYear()+'-'+this.currDate.getMonth()+'-'+(this.currDate.getDate()+cMD)+'" '+'align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:pointer;';
if (dayInList){calStr+=' background:#006699; color:#FFFFFF;';
}else {calStr+=' background:#FFFFFF; color:#808080;';
}}if (validFrom==false ||validTo==false){calStr+=' text-decoration:line-through;';
}if (validFrom==true&&validTo==true){calStr+='" '+'onMouseOver="__calendars['+this.arrayID+'].toggleDayHilight(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, true);" ';
}if (validFrom==true&&validTo==true){// 'onClick="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' +
calStr+='" '+'onMouseOut="__calendars['+this.arrayID+'].toggleDayHilight(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, false);" '+'onMouseDown="__calendars['+this.arrayID+'].toggleDay(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, \'down\');" '+'onMouseUp="__calendars['+this.arrayID+'].toggleDay(['+this.currDate.getFullYear()+','+this.currDate.getMonth()+','+(this.currDate.getDate()+cMD)+'], this, \'up\');">'+(this.currDate.getDate()+cMD)+'</td>';
}else {calStr+='">'+(this.currDate.getDate()+cMD)+'</td>';
}dayCounter++;
if ((dayCounter%7)==0){calStr+='</tr>';
if (cMD!=dCount.getDate()){'<tr>';
}}}// display the tail end of the month
for (var eMo=1; eMo<=(6-dCount.getDay()); eMo++){calStr+='<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #FFFFFF; text-align:center; cursor:default;">'+eMo+'</td>';
dayCounter++;
}calStr+='</table>'+'</td>'+'</tr>'+'<tr>'+'<td colspan=2 id="__cal'+this.arrayID+'__message" align="left" style="font-family:Tahoma; font-size:7pt; padding:2px;">'+'Select a date from the calendar above'+'</td>'+'</tr>'+'</table>'+'</td>'+'<td style="background:url(mr.gif) right top repeat-y; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>'+'</tr>'+'<tr>'+'<td style="background:url(bl.gif) no-repeat; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>'+'<td style="background:url(br.gif) right top no-repeat; height:6px; width:6px;"><img width="6" height="6" border=0 src="_blank.gif"></td>'+'</tr>'+'</table>';
this.currDate.setMonth((this.currDate.getMonth()-1));
if (typeof(alreadyLoaded)!='undefined'&&alreadyLoaded==true){document.getElementById(this.uniqueName).innerHTML=calStr;
}else {return '<div id="'+this.uniqueName+'">'+calStr+'</div>';
}}Calendar.prototype.toggleDay=function (day, divBlock, buttonEvent){var dayInList=(-1);
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==day[0]&&this.selectedDays[dInd][1]==day[1]&&this.selectedDays[dInd][2]==day[2]){dayInList=dInd;
}}if (dayInList==(-1)&&this.startTally.length==0&&buttonEvent=='down'){divBlock.style.background='#006699';
divBlock.style.color='#FFFFFF';
this.selectedDays.push(day);
this.startTally=day;
this.toggleDayHilight(day, divBlock, true);
this.selectedDays.sort(function(a, b){return (new Date(a[0], a[1], a[2])).getTime()-(new Date(b[0], b[1], b[2])).getTime()})}else if (this.startTally.length > 0&&buttonEvent=='up'){var countDays=1;
var tallyDest=new Date(day[0], (parseInt(day[1])+0), day[2]);
var tallyNew=new Date(this.startTally[0], (parseInt(this.startTally[1])+0), this.startTally[2]);
var tallyPivot=new Date(this.startTally[0], (parseInt(this.startTally[1])+0), this.startTally[2]);
while(tallyNew.getFullYear()!=tallyDest.getFullYear()||tallyNew.getMonth()!=tallyDest.getMonth()||tallyNew.getDate()!=tallyDest.getDate()){countDays++;
if (tallyNew.getTime() < tallyDest.getTime()){tallyNew.setDate(tallyNew.getDate()+1);
}else {tallyNew.setDate(tallyNew.getDate()-1);
}var newDayInList=false;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==tallyNew.getFullYear()&&this.selectedDays[dInd][1]==tallyNew.getMonth()&&this.selectedDays[dInd][2]==tallyNew.getDate()){newDayInList=true;
}}var newDivBlock=document.getElementById('__cal'+this.arrayID+'__'+tallyNew.getFullYear()+'-'+tallyNew.getMonth()+'-'+tallyNew.getDate());
if (!newDayInList&&countDays<=this.maxDays&&newDivBlock){this.selectedDays.push([tallyNew.getFullYear(), tallyNew.getMonth(), tallyNew.getDate()]);
this.selectedDays.sort(function(a, b){return (new Date(a[0], a[1], a[2])).getTime()-(new Date(b[0], b[1], b[2])).getTime()})newDivBlock.style.background='#006699';
newDivBlock.style.color='#FFFFFF';
}else if (!newDayInList&&newDivBlock){newDivBlock.style.background='#FFFFFF';
newDivBlock.style.color='#808080';
}}this.startTally=new Array();
}else if (buttonEvent=='up'){// if first day - remove whole block...
var tallyNew=new Date(day[0], (parseInt(day[1])+0), day[2]);
var tallyPrev=new Date(day[0], (parseInt(day[1])+0), day[2]);
tallyPrev.setDate(tallyPrev.getDate()-1);
var prevDayInList=false;
var remDayInList=true;
var remDayInListInd=(-1);
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==tallyPrev.getFullYear()&&this.selectedDays[dInd][1]==tallyPrev.getMonth()&&this.selectedDays[dInd][2]==tallyPrev.getDate()){prevDayInList=true;
}}if (!prevDayInList){while(remDayInList){tallyNew.setDate(tallyNew.getDate()+1);
var newDivBlock=document.getElementById('__cal'+this.arrayID+'__'+tallyNew.getFullYear()+'-'+tallyNew.getMonth()+'-'+tallyNew.getDate());
remDayInList=false;
for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++){if (this.selectedDays[rdInd][0]==tallyNew.getFullYear()&&this.selectedDays[rdInd][1]==tallyNew.getMonth()&&this.selectedDays[rdInd][2]==tallyNew.getDate()){remDayInListInd=rdInd;
remDayInList=true;
}}if (remDayInList){this.selectedDays.splice(remDayInListInd, 1);
this.selectedDays.sort(function(a, b){return (new Date(a[0], a[1], a[2])).getTime()-(new Date(b[0], b[1], b[2])).getTime()})this.toggleDayHilight([tallyNew.getFullYear(), tallyNew.getMonth(), tallyNew.getDate()], newDivBlock, false);
}}}if (dayInList!=(-1)){this.selectedDays.splice(dayInList, 1);
this.selectedDays.sort(function(a, b){return (new Date(a[0], a[1], a[2])).getTime()-(new Date(b[0], b[1], b[2])).getTime()})this.toggleDayHilight(day, divBlock, false);
}this.startTally=new Array();
}}Calendar.prototype.toggleDayHilight=function (day, divBlock, toggle){// clear selection
(document.selection) ? document.selection.empty():window.getSelection().removeAllRanges();
var userMsg=' ';
var dayInList=false;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==day[0]&&this.selectedDays[dInd][1]==day[1]&&this.selectedDays[dInd][2]==day[2]){dayInList=true;
}}if (!dayInList&&toggle){if (this.startTally.length > 0){// overflow highlighting
divBlock.style.background='#E3E3E3';
divBlock.style.color='#FFFFFF';
}else {divBlock.style.background='#008FD6';
divBlock.style.color='#FFFFFF';
userMsg='Add '+__monthsshort[day[1]]+' '+day[2]+', '+day[0];
}}else if (dayInList&&this.startTally.length > 0){userMsg='Click Again to add '+__monthsshort[day[1]]+' '+day[2]+', '+day[0];
}else if (dayInList&&this.startTally.length==0){if (toggle){divBlock.style.background='red';
divBlock.style.color='#FFFFFF';
// if first day - remove whole block...
var tallyNew=new Date(day[0], (parseInt(day[1])+0), day[2]);
var tallyPrev=new Date(day[0], (parseInt(day[1])+0), day[2]);
tallyPrev.setDate(tallyPrev.getDate()-1);
var prevDayInList=false;
var remDayInList=true;
var countDays=1;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==tallyPrev.getFullYear()&&this.selectedDays[dInd][1]==tallyPrev.getMonth()&&this.selectedDays[dInd][2]==tallyPrev.getDate()){prevDayInList=true;
}}if (!prevDayInList){while(remDayInList){tallyNew.setDate(tallyNew.getDate()+1);
var newDivBlock=document.getElementById('__cal'+this.arrayID+'__'+tallyNew.getFullYear()+'-'+tallyNew.getMonth()+'-'+tallyNew.getDate());
remDayInList=false;
for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++){if (this.selectedDays[rdInd][0]==tallyNew.getFullYear()&&this.selectedDays[rdInd][1]==tallyNew.getMonth()&&this.selectedDays[rdInd][2]==tallyNew.getDate()){remDayInList=true;
}}if (remDayInList){newDivBlock.style.background='red';
newDivBlock.style.color='#FFFFFF';
countDays++;
}}}if (countDays > 1){// nudge the tally check date back one since the iterrator will be on the day that failed to be in the cart
tallyNew.setDate(tallyNew.getDate()-1);
var startDate=new Date(day[0], (parseInt(day[1])+0), day[2]);
userMsg='Click to remove '+countDays+' days from '+__monthsshort[startDate.getMonth()]+' '+startDate.getDate()+', '+startDate.getFullYear()+' to '+__monthsshort[tallyNew.getMonth()]+' '+tallyNew.getDate()+', '+tallyNew.getFullYear();
}else {userMsg='Click to remove '+__monthsshort[day[1]]+' '+day[2]+', '+day[0];
}}else {divBlock.style.background='#006699';
divBlock.style.color='#FFFFFF';
// clear the sequence that was queued for deletion
var tallyNew=new Date(day[0], (parseInt(day[1])+0), day[2]);
var tallyPrev=new Date(day[0], (parseInt(day[1])+0), day[2]);
tallyPrev.setDate(tallyPrev.getDate()-1);
var prevDayInList=false;
var remDayInList=true;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==tallyPrev.getFullYear()&&this.selectedDays[dInd][1]==tallyPrev.getMonth()&&this.selectedDays[dInd][2]==tallyPrev.getDate()){prevDayInList=true;
}}if (!prevDayInList){while(remDayInList){tallyNew.setDate(tallyNew.getDate()+1);
var newDivBlock=document.getElementById('__cal'+this.arrayID+'__'+tallyNew.getFullYear()+'-'+tallyNew.getMonth()+'-'+tallyNew.getDate());
remDayInList=false;
for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++){if (this.selectedDays[rdInd][0]==tallyNew.getFullYear()&&this.selectedDays[rdInd][1]==tallyNew.getMonth()&&this.selectedDays[rdInd][2]==tallyNew.getDate()){remDayInList=true;
}}if (remDayInList){newDivBlock.style.background='#006699';
newDivBlock.style.color='#FFFFFF';
}}}}}else if (!dayInList&& !toggle){divBlock.style.background='#FFFFFF';
divBlock.style.color='#808080';
}var countDays=1;
var maxWarning=false;
if (this.startTally.length > 0){var tallyDest=new Date(day[0], (parseInt(day[1])+0), day[2]);
var tallyNew=new Date(this.startTally[0], (parseInt(this.startTally[1])+0), this.startTally[2]);
var tallyPivot=new Date(this.startTally[0], (parseInt(this.startTally[1])+0), this.startTally[2]);
while(tallyNew.getFullYear()!=tallyDest.getFullYear()||tallyNew.getMonth()!=tallyDest.getMonth()||tallyNew.getDate()!=tallyDest.getDate()){countDays++;
if (tallyNew.getTime() < tallyDest.getTime()){tallyNew.setDate(tallyNew.getDate()+1);
}else {tallyNew.setDate(tallyNew.getDate()-1);
}var newDivBlock=document.getElementById('__cal'+this.arrayID+'__'+tallyNew.getFullYear()+'-'+tallyNew.getMonth()+'-'+tallyNew.getDate());
dayInList=false;
for (var dInd=0; dInd < this.selectedDays.length; dInd++){if (this.selectedDays[dInd][0]==tallyNew.getFullYear()&&this.selectedDays[dInd][1]==tallyNew.getMonth()&&this.selectedDays[dInd][2]==tallyNew.getDate()){dayInList=true;
}}if (countDays<=this.maxDays){if (newDivBlock){if (!dayInList&&toggle){newDivBlock.style.background='#008FD6';
newDivBlock.style.color='#FFFFFF';
}else if (!dayInList&& !toggle){newDivBlock.style.background='#FFFFFF';
newDivBlock.style.color='#808080';
}}if ((new Date(day[0], (parseInt(day[1])+0), day[2])).getTime() < tallyPivot.getTime()){userMsg='Click to add '+countDays+' days from '+__monthsshort[tallyNew.getMonth()]+' '+tallyNew.getDate()+', '+tallyNew.getFullYear()+' to '+__monthsshort[tallyPivot.getMonth()]+' '+tallyPivot.getDate()+', '+tallyPivot.getFullYear();
}else {userMsg='Click to add '+countDays+' days from '+__monthsshort[tallyPivot.getMonth()]+' '+tallyPivot.getDate()+', '+tallyPivot.getFullYear()+' to '+__monthsshort[tallyNew.getMonth()]+' '+tallyNew.getDate()+', '+tallyNew.getFullYear();
}}else if (newDivBlock){if (!dayInList&&toggle){// overflow highlighting
newDivBlock.style.background='#EEEEEE';
newDivBlock.style.color='#808080';
if (!maxWarning){userMsg+='<BR>Cannot choose more than '+this.maxDays+' days.'maxWarning=true;
}}else if (!dayInList&& !toggle){newDivBlock.style.background='#FFFFFF';
newDivBlock.style.color='#808080';
}}}}document.getElementById('__cal'+this.arrayID+'__message').innerHTML=userMsg;
}
Here are the images used in the source (SHIFT+CLICK to download):
I added direct links to all the images used in the source at the bottom of the post. The rounded-corner grey square is an auto-sizing table that I used for my tooltips DHTML project.
Just updated a Fix to the display of August 2007 month with 8/30 (today) as the current date - was throwing off the days that "trail" the current month.
I simply added line 76 to the calendar.js file above to decrement the dCount Date object back a month. This should not adversely affect your code - but suggested to patch it!
10/31/2007 04:23PM | K. Ng
Found a bug on the calendar codes while trying to use it to implement a project. The dates calculation messed up. Try to run it today "10/31/2007" or set the date on your OS to 10/31/2007 and you'll see what I mean.
K. Ng - good catch. I noticed it working with this current month in my testbed.
I've added lots of features to this calendar to make it more of a kludge than the first version - which adds to the risk of something coded improperly.
I fixed the above bug by added a new tmpNextMo declaration near the top of the calendar.js drawCal function. I left the flawed bit of code in there to help with the change. The problem arose where if the first month, i.e. the current month, had more days than the next month it would skip the next month. In this case Oct has more days than Nov hence why it skipped to Dec.
I'll probably post another version of this - seeing as I've been fixing and changing the interactivity of this code - and made it less of a kludge.
Thanks!
03/31/2008 02:34PM | Shawn
very clean calendar, best DIV cal I've found.. one more nit, a left over bug from the Mar/April i believe.. The April 08 is reporting the tail end of March as ending on the 30th, great product.. thanks so much.
09/17/2008 12:47PM | David
Hi!
I would like to know how to set or unset programmatically one or more dates.
And I would like to know how to read set dates to store, for example, in a database.
Thank you very much!
02/03/2009 12:10PM | Noah
Hi!
I would like to know how to set or unset programmatically one or more dates.
And I would like to know how to read set dates to store, for example, in a database.
03/08/2011 08:48PM | jamie
This looks like the beginnings of EXACTLY the type of selector I need... except... (don't you hate those?):
1. I'd like to be able to create a grid of months, user-selectable layout (1x12, 3x4, 4x3, 2x6, 3x3 with selectable start/end months, etc.).
2. I'd really like this to be a jQuery-compatible thing. Doesn't have to be a plug-in, per se, but should use $("#id").val() instead of .innerHTML, or some such for cross-browser compatibility.
Have you had any thoughts for those types of changes?
I've looked at your other date-picker that _is_ a jQuery plug-in, but prefer the behavior of this one, instead.
I spent ages searching for a date-picker that didn't have loads of JS libraries, etc, and this was spot on.
One minor thing - could you please also post the background gif for the elegant rounded-corner grey square behind the calendar?
(I believe it's "/images/readon_black.gif" but we can't download that part).
Thanks again !
I added direct links to all the images used in the source at the bottom of the post. The rounded-corner grey square is an auto-sizing table that I used for my tooltips DHTML project.
I simply added line 76 to the calendar.js file above to decrement the dCount Date object back a month. This should not adversely affect your code - but suggested to patch it!
I've added lots of features to this calendar to make it more of a kludge than the first version - which adds to the risk of something coded improperly.
I fixed the above bug by added a new tmpNextMo declaration near the top of the calendar.js drawCal function. I left the flawed bit of code in there to help with the change. The problem arose where if the first month, i.e. the current month, had more days than the next month it would skip the next month. In this case Oct has more days than Nov hence why it skipped to Dec.
I'll probably post another version of this - seeing as I've been fixing and changing the interactivity of this code - and made it less of a kludge.
Thanks!
I would like to know how to set or unset programmatically one or more dates.
And I would like to know how to read set dates to store, for example, in a database.
Thank you very much!
I would like to know how to set or unset programmatically one or more dates.
And I would like to know how to read set dates to store, for example, in a database.
1. I'd like to be able to create a grid of months, user-selectable layout (1x12, 3x4, 4x3, 2x6, 3x3 with selectable start/end months, etc.).
2. I'd really like this to be a jQuery-compatible thing. Doesn't have to be a plug-in, per se, but should use $("#id").val() instead of .innerHTML, or some such for cross-browser compatibility.
Have you had any thoughts for those types of changes?
I've looked at your other date-picker that _is_ a jQuery plug-in, but prefer the behavior of this one, instead.