Javascript
Calendar Date Selection

By David R. Tribble
Revision 1.5, 2008-02-18


Description

The JavaScript code below displays a selectable calendar month. The month is initialized to display the current month, with the current day highlighted. A specific day in the calendar can be selected. The month and the year can be incremented or decremented.

Notes

Selecting a given day in the month invokes function ZselectCell(), which displays the selected date in an alert box.

Incrementing and decrementing the month or year invokes functions ZrollMonth() and ZrollYear(), respectively. They adjust the date, and then invoke function ZredrawCalendar() which re-renders the calendar cells.

Selecting the Reset button invokes function Zreset(), which reverts the displayed calendar back to the current month.

Selecting the Cancel button invokes function Zcancel().

Selecting a specific day of the month sets the following hidden form variables in the HTML document:

Variable Value Notes
ZVdate yyyy-mm-dd ISO 8601 format
ZVyear yyyy 4-digit year
ZVmonth [m]m 1 to 12
ZVday [d]d 1 to 31
ZVwkday n 0=Sun, 6=Sat

Rendered Appearance

Month
YYYY
Su Mo Tu We Th Fr Sa
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Source Code

<noscript>
 <font color='#CF0000'>
 <b>You do not have JavaScript enabled in your browser.</b>
 </font>
</noscript>
<!-- Text -->
<!-- These hidden values are set when the user selects a date -->
<form>
 <input id="ZVdate"  type="hidden" value=""/>
 <input id="ZVyear"  type="hidden" value=""/>
 <input id="ZVmonth" type="hidden" value=""/>
 <input id="ZVday"   type="hidden" value=""/>
 <input id="ZVwkday" type="hidden" value=""/>
</form>

<!-- This displays the selectable calendar -->
<p align="center">
<table id="ZDCalendarHdr" cols="2" border="1" width="300px">
 <!-- Month and year selections -->
 <tr bgcolor="#D0E8FF">
  <!-- Month selection -->
  <td width="50%">
   <table cols="2" border="0">
    <tr>
     <th id="ZDmonth" rowspan="2" title="Month"> Month </th>
     <th width="10%">
      <input type="button" value="&nbsp;+&nbsp;"
        style="background:#A0C0E0"
        title="Increment month" onclick="ZrollMonth(+1)"/>
    </tr>
    <tr>
     <th>
      <input type="button" value="&nbsp;&minus;&nbsp;"
        style="background:#A0C0E0"
        title="Decrement month" onclick="ZrollMonth(-1)"/> </th>
    </tr>
   </table>
  </td>

  <!-- Year selection -->
  <td width="50%">
   <table cols="2" border="0">
    <tr>
     <th id="ZDyear" rowspan="2" title="Year"> YYYY </th>
     <th width="10%">
      <input type="button" value="&nbsp;+&nbsp;"
        style="background:#A0C0E0"
        title="Increment year" onclick="ZrollYear(+1)"/> </th>
     <th width="10%">
      <input type="button" value="++"
        style="background:#A0C0E0"
        title="Increment year by ten" onclick="ZrollYear(+10)"/> </th>
    </tr>
    <tr>
     <th>
      <input type="button" value="&nbsp;&minus;&nbsp;"
        style="background:#A0C0E0"
        title="Decrement year" onclick="ZrollYear(-1)"/> </th>
     <th>
      <input type="button" value="&minus;&minus;"
        style="background:#A0C0E0"
        title="Decrement year by ten" onclick="ZrollYear(-10)"/> </th>
    </tr>
   </table>
  </td>
 </tr>
</table>

<table id="ZDCalendar" cols="7" border="1" width="300px">
 <!-- Weekday title row -->
 <tr bgcolor="#C0FFD0">
  <th width="14%"> Su</th>
  <th width="14%"> Mo</th>
  <th width="14%"> Tu</th>
  <th width="14%"> We</th>
  <th width="14%"> Th</th>
  <th width="14%"> Fr</th>
  <th width="14%"> Sa</th>
 </tr>

 <!-- Calendar day cells -->
 <tr id="Zrow/1" align="center">
  <td id="cell/1/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/1/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
 <tr id="Zrow/2" align="center">
  <td id="cell/2/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/2/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
 <tr id="Zrow/3" align="center">
  <td id="cell/3/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/3/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
 <tr id="Zrow/4" align="center">
  <td id="cell/4/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/4/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
 <tr id="Zrow/5" align="center">
  <td id="cell/5/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/5/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
 <tr id="Zrow/6" align="center">
  <td id="cell/6/1" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/2" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/3" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/4" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/5" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/6" onclick="ZselectCell(this)"/>.<br/></td>
  <td id="cell/6/7" onclick="ZselectCell(this)"/>.<br/></td>
 </tr>
</table>

<!-- Reset and Cancel buttons -->
<table cols="1" border="0" width="300px">
 <tr>
  <td align="right">
   <input type="button" style="background:#A0C0E0" value="Reset"
     title="Reset to today's date" onclick="Zreset()"/>
   <input type="button" style="background:#A0C0E0" value="Cancel"
     title="Cancel" onclick="Zcancel()"/>
  </td>
 </tr>
</table>
</p>
<script type="text/javascript" language="JavaScript">
//<!--
// jscalendar.html
//
// This is known to work with:
//   Netscape 7.0, 7.2
//   FireFox 2.0
//   MS Internet Explorer 6.0, 7.0
//
// Copyright ©2007-2008 by David R. Tribble, all rights reserved.
// Permission is granted to anyone except members of organizations designated
// as terrorist organizations by the U.S. State Department to freely use,
// duplicate, and modify this source code.

// Initialize
var Zinfo = new Object();
Zinfo.Zcell = null;
Zinfo.Zcellbg = "#FFFFFF";
Zinfo.Zdate = new Date();
Zinfo.Zmonths =
[
  "January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"
];

ZredrawCalendar();

function ZformatDate()
{
  var yr, mo, dy;

  // Format the selected date as "yyyy-mm-dd"
  yr = Zinfo.Zdate.getFullYear();
  mo = Zinfo.Zdate.getMonth() + 1;
  mo = (mo < 10 ? "0"+mo : mo);
  dy = Zinfo.Zdate.getDate();
  dy = (dy < 10 ? "0"+dy : dy);
  var r = yr + "-" + mo + "-" + dy;
  return (r);
}

function ZrollMonth(inc)
{
  // Increment/decrement the displayed month
  var yr, mo;
  yr = Zinfo.Zdate.getFullYear();
  mo = Zinfo.Zdate.getMonth() + inc;
  if (yr >= 2400  &&  mo > 11)
    return;
  if (yr <= 1600  &&  mo < 0)
    return;
  Zinfo.Zdate.setDate(1);
  Zinfo.Zdate.setMonth(mo);
  ZredrawCalendar();
}

function ZrollYear(inc)
{
  // Increment/decrement the displayed year
  var yr;
  yr = Zinfo.Zdate.getFullYear() + inc;
  if (yr > 2400)
    yr = 2400;
  if (yr < 1600)
    yr = 1600;
  Zinfo.Zdate.setDate(1);
  Zinfo.Zdate.setFullYear(yr);
  ZredrawCalendar();
}

function ZselectCell(cell)
{
  // User selected a day cell
  try
  {
    var o, mday;
    var vdate, vyr, vmon, vday, vwday;

    // Retrieve the selected cell
    mday = cell.firstChild.nodeValue;
    if (mday == ""  ||  mday == 0)
      return;

    // Mark the selected cell
    if (Zinfo.Zcell != null)
      Zinfo.Zcell.style.background = Zinfo.Zcellbg;
    Zinfo.Zcell = cell;
    Zinfo.Zcellbg = Zinfo.Zcell.style.background;
    Zinfo.Zcell.style.background = "#FFD060";

    // Set the hidden form elements
    Zinfo.Zdate.setDate(mday);
    o = document.getElementById("ZVdate");
    vdate = ZformatDate();
    o.value = vdate;
    o = document.getElementById("ZVyear");
    vyr = Zinfo.Zdate.getFullYear();
    o.value = vyr;
    o = document.getElementById("ZVmonth");
    vmon = Zinfo.Zdate.getMonth() + 1;
    o.value = vmon;
    o = document.getElementById("ZVday");
    vday = Zinfo.Zdate.getDate();
    o.value = vday;
    o = document.getElementById("ZVwkday");
    vwday = Zinfo.Zdate.getDay();
    o.value = vwday;

    // Inform the user
    alert("Selected: " + vdate + "\r\n\r\n"
      + "ZVdate=" +  vdate + "\r\n"
      + "ZVyear=" +  vyr + "\r\n"
      + "ZVmonth=" + vmon + "\r\n"
      + "ZVday=" +   vday + "\r\n"
      + "ZVwkday=" + vwday);
  }
  catch (ex)
  {
    alert("ZselectCell: Exception: " + ex.description);
  }
}

function ZredrawCalendar()
{
  try
  {
    var o, v;

    // Update the displayed year
    o = document.getElementById("ZDyear");
    v = Zinfo.Zdate.getFullYear();
    o.firstChild.nodeValue = v;
    o.title = v;

    // Update the displayed month
    v = Zinfo.Zmonths[Zinfo.Zdate.getMonth()];
    o = document.getElementById("ZDmonth");
    o.firstChild.nodeValue = v;
    o.title = v;

    // Reset all cells to empty
    Zinfo.Zcell = null;
    for (var i = 1;  i <= 6;  i++)
      for (var j = 1;  j <= 7;  j++)
        ZsetCell(i, j, 0, "#F0F0F0");

    // Update the calendar day cells
    var d2 = new Date();
    var today = d2.getDate();
    if (d2.getFullYear() != Zinfo.Zdate.getFullYear())
      today = 0;
    if (d2.getMonth() != Zinfo.Zdate.getMonth())
      today = 0;

    d2.setDate(1);
    d2.setFullYear(Zinfo.Zdate.getFullYear());
    d2.setMonth(Zinfo.Zdate.getMonth() + 1);
    d2.setDate(0);
    var maxd = d2.getDate();

    d2.setMonth(Zinfo.Zdate.getMonth());
    d2.setDate(1);
    var c = 1 - d2.getDay();

    for (var i = 1;  i <= 6;  i++)
    {
      for (var j = 1;  j <= 7;  j++)
      {
        if (c < 1  ||  c > maxd)
          ZsetCell(i, j, 0, "#F0F0F0");
        else if (c == today)
          ZsetCell(i, j, c, "#FFFF80");
        else
          ZsetCell(i, j, c, "#FFFFD0");
        c++;
      }
    }
  }
  catch (ex)
  {
    alert("ZredrawCalendar: Exception: " + ex.description);
  }
}

function ZsetCell(row, col, mday, bg)
{
  var o = document.getElementById("cell/" + row + "/" + col);
  o.firstChild.nodeValue = (mday < 1 ? "" : mday);
  o.style.background = bg;
}

function Zreset()
{
  // User clicked the "Reset" button
  //alert("RESET");
  Zinfo.Zdate = new Date();
  ZredrawCalendar();
}

function Zcancel()
{
  // User clicked the "Cancel" button
  alert("CANCEL");
}
//-->
</script>


Copyright ©2007-2008 by David R. Tribble, all rights reserved.
Permission is granted to anyone to link to this document and to quote portions of it without restriction. Permission is granted to anyone except members of organizations designated as terrorist organizations by the U.S. State Department to freely use, duplicate, and modify the source code.