You are here: irt.org | Articles | JavaScript | Date and Time | Calendars [ next ]
Published on: Sunday 1st December 1996 By: Martin Webb
What we want to be able to show is a 12 month calendar from January to December in a grid 4 by 3, with the current day highlighted.
We will achieve this requirement in stages. Each month will be output by a JavaScript function called Calendar by passing the month and year e.g.:
Calendar(m,y)
Where m is the month from 0 to 11, and y is the year. JavaScript dates store January as '0' and December as '11'. The Calendar function will handle all the required processing, i.e. to calculate the day the first of the required month falls on, and whether the year is a leap year or not.
To calculate the day the first of a month falls on, first set the date to the 1st of the month and then use the getDay() function to return the day:
var firstDay = new Date(Year,Month,1); var startDay = firstDay.getDay();
When creating a date object the new keyword is used, if no parameters are passed to the Date() function then the new date object will be set to the current date.
Year is a leap year if it is divisible by 4, and not by 100, unless it is divisible by 400:
if (((Year % 4 == 0) && (Year % 100 != 0)) || (Year % 400 == 0)) days[1] = 29; else days[1] = 28;
The days[] variable is an array that holds the number of days of each month. This can be easily created with the following function definition and function call which creates a new array object. The use of this allows reference to the variable days:
function array( m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 ) { this[0] = m0; this[1] = m1; this[2] = m2; this[3] = m3; this[4] = m4; this[5] = m5; this[6] = m6; this[7] = m7; this[8] = m8; this[9] = m9; this[10] = m10; this[11] = m11; } var days = new array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
The array() function can be reused to create the names[] array to hold the names of each month:
var names = new array( 'Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec' );
All that remains is to output the calendar within an HTML table:
document.write('<table callspacing="0" cellpadding="0" border="1">'); document.write('<tr align="right">'); var column = 0; for (i=0; i<startDay; i++) { document.write('<td> </td>'); column++; }
This creates a table, starts the first row, and creates empty cells until the startDay, i.e. first day of the month. The column variable controls the number of days displayed across the table.
for (i=1; i<=days[Month]; i++) { document.write('<td>',i,'</td>'); column++; if (column == 7) { document.write('</tr><tr align="right">'); column = 0; } } document.write('</tr></table>');
This outputs all the days in the Month in individual cells. When a whole week is output, the current table row is ended and a new table row is started. Finally the last table row and the table are ended.
The first draft of the Calender JavaScript function is now complete:
<script language="JavaScript"><!-- function Calendar(Month,Year) { firstDay = new Date(Year,Month,1); startDay = firstDay.getDay(); if (((Year % 4 == 0) && (Year % 100 != 0)) || (Year % 400 == 0)) days[1] = 29; else days[1] = 28; document.write( '<table callspacing="0" cellpadding="0" border="1">' + '<tr align=right>' ); var column = 0; for (i=0; i<startDay; i++) { document.write('<td> </td>'); column++; } for (i=1; i<=days[Month]; i++) { document.write('<td>',i,'</td>'); column++; if (column == 7) { document.write('</tr><tr align="right">'); column = 0; } } document.write('</tr></table>'); } function array( m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 ) { this[0] = m0; this[1] = m1; this[2] = m2; this[3] = m3; this[4] = m4; this[5] = m5; this[6] = m6; this[7] = m7; this[8] = m8; this[9] = m9; this[10] = m10; this[11] = m11; } var names = new array( 'Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec' ); var days = new array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); Calendar(1-1,1997); //--></script>
This then displays the following:
This basic Calendar function can then be enhanced to show the month, year and days of the week with the addition of the following:
document.write( '<tr><th colspan=7>' + names[Month] + ' ' + Year ); document.write( '<tr>' + '<th>S</th>' + '<th>M</th>' + '<th>T</th>' + '<th>W</th>' + '<th>T</th>' + '<th>F</th>' + '<th>S</th>' + '</tr>' );
Color can be added for those browsers that support table cell colors. First we need to define some colors to be used before calling the Calendar function:
var thcol = 'bgcolor="#AAACBF"'; var trcol = 'bgcolor="#CCECFF"'; var tdcol = 'bgcolor="#FF8888"';
Then whenever we use <tr> or <td> to start a new table row or cell add the additional Bgcolor attribute, e.g.:
document.write( '<tr><th colspan="7">' + names[Month] + ' ' +Year );
replace with:
document.write( '<tr ' + thcol + '><th colspan="7">' + names[Month] + ' ' + Year );
which when written to the document will appear as:
<tr bgcolor='#AAACBF'><th colspan="7">Jan 1997);
To highlight the current day we need to define another color and show the calendar for the current month and year:
var hlcol = "bgcolor='#8888ff'"; function y2k(number) { return (number < 1000) ? number + 1900 : number; } var today = new Date(); var thisDay = today.getDate(); var thisMonth = today.getMonth(); var thisYear = y2k(today.getYear()); Calendar(thisMonth,thisYear);
The JavaScript function getYear() returns the number of years since 1900, or the year if greater than 2000, therefore we use the y2k() function to force the year into CCYY format. As thisMonth is a value calculated by the getMonth() function, we no longer have to subtract 1 from the year.
When writing each day to the table cell if it is equal to today's date use the new color instead:
if ((i == thisDay) && (Month == thisMonth) && (Year == thisYear)) document.write('<td ' + hlcol + '>' + i + '</td>'); else document.write('<td ' + tdcol + '>' + i + '</td>');
The calendar for the current month and year now looks like this:
To display the current years calendar the following JavaScript can be used to call the Calendar function twelve times:
document.write('<table ><tr valign="top"><td>'); Calendar(1-1,thisYear); document.write('</td><td>'); Calendar(2-1,thisYear); document.write('</td><td>'); Calendar(3-1,thisYear); document.write('</td><td>'); Calendar(4-1,thisYear); document.write('</td></tr><tr valign="top"><td>'); Calendar(5-1,thisYear); document.write('</td><td>'); Calendar(6-1,thisYear); document.write('</td><td>'); Calendar(7-1,thisYear); document.write('</td><td>'); Calendar(8-1,thisYear); document.write('</td></tr><tr valign="top"><td>'); Calendar(9-1,thisYear); document.write('</td><td>'); Calendar(10-1,thisYear); document.write('</td><td>'); Calendar(11-1,thisYear); document.write('</td><td>'); Calendar(12-1,thisYear); document.write('</td></tr></table>');
Since the introduction of Netscape Navigator 4 - using multiple document.write's in a script slows down the rendering of the page, so much so, that the above example can take almost 30 seconds to display. There is a simple answer to this - use fewer document writes. The following is a rewritten version of the calendar script:
<script language="JavaScript"><!-- function Calendar(Month,Year) { var output = ''; firstDay = new Date(Year,Month,1); startDay = firstDay.getDay(); if (((Year % 4 == 0) && (Year % 100 != 0)) || (Year % 400 == 0)) days[1] = 29; else days[1] = 28; output += '<table callspacing="0" cellpadding="0">' + '<tr ' + thcol + '>' + '<th colspan="7">' + names[Month] + ' ' + Year + '<tr ' + trcol + '>' + '<th>S</th>' + '<th>M</th>' + '<th>T</th>' + '<th>W</th>' + '<th>T</th>' + '<th>F</th>' + '<th>S</th>' + '</tr>' + '<tr align="right">'; var column = 0; for (i=0; i<startDay; i++) { output += '<td> </td>'; column++; } for (i=1; i<=days[Month]; i++) { if ((i == thisDay) && (Month == thisMonth) && (Year == thisYear)) output += '<td ' + hlcol + '>' + i + '</td>'; else output += '<td ' + tdcol + '>' + i + '</td>'; column++; if (column == 7) { output += '</tr><tr align="right">'; column = 0; } } output += '</tr></table>'; return output; } function array( m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11 ) { this[0] = m0; this[1] = m1; this[2] = m2; this[3] = m3; this[4] = m4; this[5] = m5; this[6] = m6; this[7] = m7; this[8] = m8; this[9] = m9; this[10] = m10; this[11] = m11; } var names = new array( 'Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec' ); var days = new array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); var thcol = 'bgcolor="#AAACBF"'; var trcol = 'bgcolor="#CCECFF"'; var tdcol = 'bgcolor="#FF8888"'; var hlcol = 'bgcolor="#8888ff"'; function y2k(number) { return (number < 1000) ? number + 1900 : number; } var today = new Date(); var thisDay = today.getDate(); var thisMonth = today.getMonth(); var thisYear = y2k(today.getYear()); var output = '<table border><tr valign="top"><td>' + Calendar(1-1,thisYear) + '</td><td>' + Calendar(2-1,thisYear) + '</td><td>' + Calendar(3-1,thisYear) + '</td><td>' + Calendar(4-1,thisYear) + '</td></tr><tr valign="top"><td>' + Calendar(5-1,thisYear) + '</td><td>' + Calendar(6-1,thisYear) + '</td><td>' + Calendar(7-1,thisYear) + '</td><td>' + Calendar(8-1,thisYear) + '</td></tr><tr valign="top"><td>' + Calendar(9-1,thisYear) + '</td><td>' + Calendar(10-1,thisYear) + '</td><td>' + Calendar(11-1,thisYear) + '</td><td>' + Calendar(12-1,thisYear) + '</td></tr></table>'; document.write(output); //--></script>
It uses a local output variable within the Calender() function to hold the contents of the HTML built up, which it then returns to the functions caller, which maintains a global output variable which is then written to the document with just one document.write.
Which produces the following:
The final example uses a combination of JavaScript and Forms to create a more sophisticated calendar interface. If your browser does not support floating frames, then view the calendar in a popup window.
It is created using two html files, one called calendar.htm, which contains some JavaScript to set the initial month and year variables and a Frameset with one frame containing the source file cal.htm, which controls the calendar display.
The following diagram illustrates the relationship between the two files:
calendar.htm (parent document) | | cal.htm (child document)
The child document uses the variables established in the parent document using the following syntax:
Calendar(parent.month,parent.year);
When the child document detects changes in either the Month or Year selections, using the following Select attributes:
<select name="Month" onChange="changeMonth()"> <select name="Year" onChange="changeYear()">
the JavaScript variables within the parent document are updated and the child document is redisplayed, e.g.:
function changeMonth () { if ( document.Cal.Month.options[ document.Cal.Month.selectedIndex ].value != '') { parent.month1 = document.Cal.Month.options[ document.Cal.Month.selectedIndex ].value; location.href = 'cal1.htm'; } } function changeYear () { if ( document.Cal.Year.options[ document.Cal.Year.selectedIndex ].value != '') { parent.year1 = document.Cal.Year.options[ document.Cal.Year.selectedIndex ].value; location.href = 'cal1.htm'; } }
Where document.Cal.Year represents the document followed by the name of the form followed by the name of the selection.
View the complete calendar.htm and cal.htm source code.
Monday's child is full of grace