-->
Bookmark and Share

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
             
             
             
             
             
             

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;">&nbsp;</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;">&lt;&lt;</a>
<a href="" title="Previous month."
 onclick="addMonths(event, -1); return false;">&lt;</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.