Calendar
See the demo page for the finished version of the code.Creating the Calendar
Now that we have the Date
object working the way we want, we
can put it to use. We'll set up a simple monthly calendar display with a few
controls allowing the user to change the month and year and select a particular
date.
The Calendar Display
A simple table is used for the calendar. Six rows and seven columns are needed to hold the days of the month underneath a couple of headers for displaying the month name and year and the days of the week. The last row of the table holds some links which will be set up to allow the user to control the display.
Month Year | ||||||
---|---|---|---|---|---|---|
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
<< < | Select Reset | > >> |
Styles can be set up to give the display more character (see the demo for one example). But there are a some pieces that need to be put into place.
First, the TABLE tag is given an ID of "calendar" and the cell for the
month name and year display in the first row is given an ID of
"calendarHeader." This will allow those elements to be easily accessed by the
script. Second, each date cell will contain a link with and
onclick
event handler defined as follows:
<td> <a href="" onclick="setTargetDate(event, this); return false;"> </a> </td>
This will allow the user to click on any date to select it. Likewise, the
links in the bottom row use onclick
to fire functions
corresponding to their purpose:
<a href="" title="Previous year." onclick="addYears(event, -1); return false;"><<</a> <a href="" title="Previous month." onclick="addMonths(event, -1); return false;"><</a> ...
These functions will be detailed later on. First we'll look at setting up the calendar itself.
Script Initialization
In addition to the code for extending the Date
object we'll
need to start off by setting a target date and initialize the calendar
display.
// Default target date to today. var targetDate = new Date(); // Initialize the calendar display once the page loads. window.onload = setCalendar;
The global variable targetDate
will be used to hold the
currently selected date. It is initially set to the current date. Next, an
onload
handler for the window is set to run a function called
setCalendar()
once the page has loaded.
Filling in the Calendar
setCalendar()
is the heart of the script. It dynamically
updates the calendar display to match the date held in
targetDate.
Its first action is to hide the page display. Mozilla browsers tend to
display artifacts when table elements are dynamically modifed. Toggling the
display
style property of the page corrects this problem.
function setCalendar(event) { var el, tableEl, rowEl, cellEl, linkEl; var tmpDate, tmpDate2; var i, j; // Force a redraw by hiding the entire page. document.body.style.display = "none";
The next thing the function does is to set the month and year. Using
document.getElementById()
it locates the top table cell and
updates the text with the target month name and year.
// Update month name. el = document.getElementById("monthYear").firstChild; el.nodeValue = targetDate.getMonthName() + "\u00a0" + targetDate.getFullYear();
Next it creates a temporary date object by copying the target date. This temporary date will be used to fill in the days of the month on the display by looping through the table, doing one row of dates at a time. Likewise, for each row, it will loop through each cell in that row.
Finding the Starting Date
The target date can be any day in the month. But we need to start with the first Sunday on or before the first of the month because that's where the display starts. For example, August 1, 2001 falls on a Wednesday. So the display should look like the sample below.
August 2001 | ||||||
---|---|---|---|---|---|---|
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
1 | 2 | 3 | 4 |
That means that the first cell of the first date row corresponds to Sunday,
July 29, 2001 and that's the date it needs to start with. To accomplish this,
The function starts by setting the temporary date to the first of the
month. If that day is not a Sunday, it subtracts one day from the date using
our new Date
method, addDays(),
repeating this until
it reaches a date that falls on Sunday.
// Start with the first day of the month and go back if necessary to // the previous Sunday. tmpDate = new Date(Date.parse(targetDate)); tmpDate.setDate(1); while (tmpDate.getDay() != 0) { tmpDate.addDays(-1); }
Now it can start looping through the rows and cells of the table setting dates. First, it locates the TABLE element with the ID "calendar" and sets a loop for the third through eighth rows (recall that the first two rows are used for displaying the month name and year and the days of the week).
The Row Loop
// Go through each calendar day cell in the table and update it. tableEl = document.getElementById("calendar"); for (i = 2; i <= 7; i++) { rowEl = tableEl.rows[i];
The first thing we want to do is determine if the row even needs to be displayed. Depending on the particular month, we may need as little as four rows or as many as six to display all the days. For example, February 1998 requires only four rows,
February 1998 | ||||||
---|---|---|---|---|---|---|
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
while May 1998 requires all six
May 1998 | ||||||
---|---|---|---|---|---|---|
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
So the function compares the current temporary date with the target date. If the temporary date is not in the target month, it assigns the "empty" style class to the row which will hide it.
// Hide row if it contains no dates in the current month. tmpDate2 = new Date(Date.parse(tmpDate)); tmpDate2.addDays(6); if (tmpDate.getMonth() != targetDate.getMonth() && tmpDate2.getMonth() != targetDate.getMonth()) { rowEl.className = "empty"; if (document.all) for (j = 0; j > rowEl.cells.length; j++) rowEl.cells[j].style.borderStyle = "none"; } else { rowEl.className = ""; if (document.all) for (j = 0; j > rowEl.cells.length; j++) rowEl.cells[j].style.borderStyle = ""; }
Likewise, if the row does contain dates in the target month, the class is removed, allowing the row to show.