Popup Calendar for a textfield : Calendar « GUI Components « JavaScript DHTML

JavaScript DHTML
1. Ajax Layer
2. Data Type
3. Date Time
4. Development
5. Document
6. Event
7. Event onMethod
8. Form Control
9. GUI Components
10. HTML
11. Javascript Collections
12. Javascript Objects
13. Language Basics
14. Node Operation
15. Object Oriented
16. Page Components
17. Security
18. Style Layout
19. Table
20. Utilities
21. Window Browser
Microsoft Office Word 2007 Tutorial
Java
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
PHP
Python
SQL Server / T-SQL
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
JavaScript DHTML » GUI Components » Calendar 
Popup Calendar for a textfield

<html>
<head>
<title>Minimum Required Code - Epoch DHTML Javascript Calendar</title>

<style rel="stylesheet" type="text/css">
table.calendar {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 0.8em;
  border-collapse: collapse;
  background-color: white;
  border: solid #999999 1px;
  background-color: white;
  width: 200px;
  text-align: center;
  /*prevent user from selecting text in Mozilla & Safari - check calendar constructor for IE code)*/
  -moz-user-select: none;
    /*-khtml-user-select: none;*/
}
table.calendar input, table.calendar select {
  font-size: 10px;
}
table.calendar td {
  border: 0;
  font-size: 10px;
  text-align: center;
}
div.mainheading {
  margin: 2px;
}
table.caldayheading {
  border-collapse: collapse;
  cursor: pointer;
  empty-cells: show;
  margin: 0 6px 0 6px;
}
table.caldayheading td {
  border: solid #CCCCCC 1px;
  text-align: left;
  color: #0054E3;
  font-weight: bold;
  width: 22px; /*should match calendar cell's width*/
}
table.caldayheading td.wkhead {
  border-right: double #CCCCCC 3px;
}
table.calcells {
  border-collapse: collapse;
  cursor: pointer;
  margin: 0 6px 0 6px;
}
table.calcells td {
  border: solid #CCCCCC 1px;
  vertical-align: top;
  text-align: left;
  font-weight: bold;
  width: 22px;
  height: 20px; /*IE doesn't like ems*/
}
table.calcells td div {
  padding: 1px;
  margin: 0;
}
table.calcells td.wkhead {
  background-color: white;
  text-align: center;
  border-right: double #CCCCCC 3px;
  color: #0054E3;
}
table.calcells td.wkday {
  background-color: #DDDDDD;
}
table.calcells td.wkend {
  background-color: #DDDDDD;
}
table.calcells td.curdate {

}
table.calcells td.cell_selected {
  background-color: #99CCFF;
  color: black;
}
table.calcells td.notmnth {
  background-color: #FFFFFF;
  color: #CCCCCC;
}
table.calcells td.notallowed {
  background-color: white;
  color: #EEEEEE;
  font-style: italic;
}
table.calcells td.hover {
  background-color: #999999;
}

</style>
<script type="text/javascript">
/*****************************************************************************
Copyright (C) 2006  Nick Baicoianu

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*****************************************************************************/
//constructor for the main Epoch class (ENGLISH VERSION)
function Epoch(name,mode,targetelement,multiselect)
{
  this.state = 0;
  this.name = name;
  this.curDate = new Date();
  this.mode = mode;
  this.selectMultiple = (multiselect == true)//'false' is not true or not set at all
  
  //the various calendar variables
  //this.selectedDate = this.curDate;
  this.selectedDates = new Array();
  this.calendar;
  this.calHeading;
  this.calCells;
  this.rows;
  this.cols;
  this.cells = new Array();
  
  //The controls
  this.monthSelect;
  this.yearSelect;
  
  //standard initializations
  this.mousein = false;
  this.calConfig();
  this.setDays();
  this.displayYear = this.displayYearInitial;
  this.displayMonth = this.displayMonthInitial;
  
  this.createCalendar()//create the calendar DOM element and its children, and their related objects
  
  if(this.mode == 'popup' && targetelement && targetelement.type == 'text') //if the target element has been set to be an input text box
  {
    this.tgt = targetelement;
    this.calendar.style.position = 'absolute';
    this.topOffset = this.tgt.offsetHeight; // the vertical distance (in pixels) to display the calendar from the Top of its input element
    this.leftOffset = 0;           // the horizontal distance (in pixels) to display the calendar from the Left of its input element
    this.calendar.style.top = this.getTop(targetelementthis.topOffset + 'px';
    this.calendar.style.left = this.getLeft(targetelementthis.leftOffset + 'px';
    document.body.appendChild(this.calendar);
    this.tgt.calendar = this;
    this.tgt.onfocus = function () {this.calendar.show();}//the calendar will popup when the input element is focused
    this.tgt.onblur = function () {if(!this.calendar.mousein){this.calendar.hide();}}//the calendar will popup when the input element is focused
  }
  else
  {
    this.container = targetelement;
    this.container.appendChild(this.calendar);
  }
  
  this.state = 2//0: initializing, 1: redrawing, 2: finished!
  this.visible ? this.show() this.hide();
}
//-----------------------------------------------------------------------------
Epoch.prototype.calConfig = function () //PRIVATE: initialize calendar variables
{
  //this.mode = 'flat'; //can be 'flat' or 'popup'
  this.displayYearInitial = this.curDate.getFullYear()//the initial year to display on load
  this.displayMonthInitial = this.curDate.getMonth()//the initial month to display on load (0-11)
  this.rangeYearLower = 2005;
  this.rangeYearUpper = 2037;
  this.minDate = new Date(2005,0,1);
  this.maxDate = new Date(2037,0,1);
  this.startDay = 0// the day the week will 'start' on: 0(Sun) to 6(Sat)
  this.showWeeks = true//whether the week numbers will be shown
  this.selCurMonthOnly = false//allow user to only select dates in the currently displayed month
  this.clearSelectedOnChange = true//whether to clear all selected dates when changing months
  
  //flat mode-only settings:
  //this.selectMultiple = true; //whether the user can select multiple dates (flat mode only)

  switch(this.mode//set the variables based on the calendar mode
  {
    case 'popup': //popup options
      this.visible = false;
      break;
    case 'flat':
      this.visible = true;
      
      break;
  }
  this.setLang();
};
//-----------------------------------------------------------------------------
Epoch.prototype.setLang = function()  //all language settings for Epoch are made here.  Check Date.dateFormat() for the Date object's language settings
{
  this.daylist = new Array('Su','Mo','Tu','We','Th','Fr','Sa','Su','Mo','Tu','We','Th','Fr','Sa'); /*<lang:en>*/
  this.months_sh = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
  this.monthup_title = 'Go to the next month';
  this.monthdn_title = 'Go to the previous month';
  this.clearbtn_caption = 'Clear';
  this.clearbtn_title = 'Clears any dates selected on the calendar';
  this.maxrange_caption = 'This is the maximum range';
};
//-----------------------------------------------------------------------------
Epoch.prototype.getTop = function (element//PRIVATE: returns the absolute Top value of element, in pixels
{
    var oNode = element;
    var iTop = 0;
    
    while(oNode.tagName != 'BODY') {
        iTop += oNode.offsetTop;
        oNode = oNode.offsetParent;
    }
    
    return iTop;
};
//-----------------------------------------------------------------------------
Epoch.prototype.getLeft = function (element//PRIVATE: returns the absolute Left value of element, in pixels
{
    var oNode = element;
    var iLeft = 0;
    
    while(oNode.tagName != 'BODY') {
        iLeft += oNode.offsetLeft;
        oNode = oNode.offsetParent;        
    }
    
    return iLeft;
};
//-----------------------------------------------------------------------------
Epoch.prototype.show = function () //PUBLIC: displays the calendar
{
  this.calendar.style.display = 'block';
  this.visible = true;
};
//-----------------------------------------------------------------------------
Epoch.prototype.hide = function () //PUBLIC: Hides the calendar
{
  this.calendar.style.display = 'none';
  this.visible = false;
};
//-----------------------------------------------------------------------------
Epoch.prototype.toggle = function () //PUBLIC: Toggles (shows/hides) the calendar depending on its current state
{
  if(this.visible) {
    this.hide();
  }
  else {
    this.show();
  }
};
//-----------------------------------------------------------------------------
Epoch.prototype.setDays = function ()  //PRIVATE: initializes the standard Gregorian Calendar parameters
{
  this.daynames = new Array();
  var j=0;
  for(var i=this.startDay; i< this.startDay + 7;i++) {
    this.daynames[j++this.daylist[i];
  }
    
  this.monthDayCount = new Array(31,((this.curDate.getFullYear() 200028 29),31,30,31,30,31,31,30,31,30,31);
};
//-----------------------------------------------------------------------------
Epoch.prototype.setClass = function (element,className//PRIVATE: sets the CSS class of the element, W3C & IE
{
  element.setAttribute('class',className);
  element.setAttribute('className',className)//<iehack>
};
//-----------------------------------------------------------------------------
Epoch.prototype.createCalendar = function ()  //PRIVATE: creates the full DOM implementation of the calendar
{
  var tbody, tr, td;
  this.calendar = document.createElement('table');
  this.calendar.setAttribute('id',this.name+'_calendar');
  this.setClass(this.calendar,'calendar');
  //to prevent IE from selecting text when clicking on the calendar
  this.calendar.onselectstart = function() {return false;};
  this.calendar.ondrag = function() {return false;};
  tbody = document.createElement('tbody');
  
  //create the Main Calendar Heading
  tr = document.createElement('tr');
  td = document.createElement('td');
  td.appendChild(this.createMainHeading());
  tr.appendChild(td);
  tbody.appendChild(tr);
  
  //create the calendar Day Heading
  tr = document.createElement('tr');
  td = document.createElement('td');
  td.appendChild(this.createDayHeading());
  tr.appendChild(td);
  tbody.appendChild(tr);

  //create the calendar Day Cells
  tr = document.createElement('tr');
  td = document.createElement('td');
  td.setAttribute('id',this.name+'_cell_td');
  this.calCellContainer = td;  //used as a handle for manipulating the calendar cells as a whole
  td.appendChild(this.createCalCells());
  tr.appendChild(td);
  tbody.appendChild(tr);
  
  //create the calendar footer
  tr = document.createElement('tr');
  td = document.createElement('td');
  td.appendChild(this.createFooter());
  tr.appendChild(td);
  tbody.appendChild(tr);
  
  //add the tbody element to the main calendar table
  this.calendar.appendChild(tbody);

  //and add the onmouseover events to the calendar table
  this.calendar.owner = this;
  this.calendar.onmouseover = function() {this.owner.mousein = true;};
  this.calendar.onmouseout = function() {this.owner.mousein = false;};
};
//-----------------------------------------------------------------------------
Epoch.prototype.createMainHeading = function () //PRIVATE: Creates the primary calendar heading, with months & years
{
  //create the containing <div> element
  var container = document.createElement('div');
  container.setAttribute('id',this.name+'_mainheading');
  this.setClass(container,'mainheading');
  //create the child elements and other variables
  this.monthSelect = document.createElement('select');
  this.yearSelect = document.createElement('select');
  var monthDn = document.createElement('input'), monthUp = document.createElement('input');
  var opt, i;
  //fill the month select box
  for(i=0;i<12;i++)
  {
    opt = document.createElement('option');
    opt.setAttribute('value',i);
    if(this.state == && this.displayMonth == i) {
      opt.setAttribute('selected','selected');
    }
    opt.appendChild(document.createTextNode(this.months_sh[i]));
    this.monthSelect.appendChild(opt);
  }
  //and fill the year select box
  for(i=this.rangeYearLower;i<=this.rangeYearUpper;i++)
  {
    opt = document.createElement('option');
    opt.setAttribute('value',i);
    if(this.state == && this.displayYear == i) {
      opt.setAttribute('selected','selected');
    }
    opt.appendChild(document.createTextNode(i));
    this.yearSelect.appendChild(opt);    
  }
  //add the appropriate children for the month buttons
  monthUp.setAttribute('type','button');
  monthUp.setAttribute('value','>');
  monthUp.setAttribute('title',this.monthup_title);
  monthDn.setAttribute('type','button');
  monthDn.setAttribute('value','<');
  monthDn.setAttribute('title',this.monthdn_title);
  this.monthSelect.owner = this.yearSelect.owner = monthUp.owner = monthDn.owner = this;  //hack to allow us to access this calendar in the events (<fix>??)
  
  //assign the event handlers for the controls
  monthUp.onmouseup = function () {this.owner.nextMonth();};
  monthDn.onmouseup = function () {this.owner.prevMonth();};
  this.monthSelect.onchange = function() {
    this.owner.displayMonth = this.value;
    this.owner.displayYear = this.owner.yearSelect.value; 
    this.owner.goToMonth(this.owner.displayYear,this.owner.displayMonth);
  };
  this.yearSelect.onchange = function() {
    this.owner.displayMonth = this.owner.monthSelect.value;
    this.owner.displayYear = this.value; 
    this.owner.goToMonth(this.owner.displayYear,this.owner.displayMonth);
  };
  
  //and finally add the elements to the containing div
  container.appendChild(monthDn);
  container.appendChild(this.monthSelect);
  container.appendChild(this.yearSelect);
  container.appendChild(monthUp);
  return container;
};
//-----------------------------------------------------------------------------
Epoch.prototype.createFooter = function () //PRIVATE: creates the footer of the calendar - goes under the calendar cells
{
  var container = document.createElement('div');
  var clearSelected = document.createElement('input');
  clearSelected.setAttribute('type','button');
  clearSelected.setAttribute('value',this.clearbtn_caption);
  clearSelected.setAttribute('title',this.clearbtn_title);
  clearSelected.owner = this;
  clearSelected.onclick = function() { this.owner.resetSelections(false);};
  container.appendChild(clearSelected);
  return container;
};
//-----------------------------------------------------------------------------
Epoch.prototype.resetSelections = function (returnToDefaultMonth)  //PRIVATE: reset the calendar's selection variables to defaults
{
  this.selectedDates = new Array();
  this.rows = new Array(false,false,false,false,false,false,false);
  this.cols = new Array(false,false,false,false,false,false,false);
  if(this.tgt)  //if there is a target element, clear it too
  {
    this.tgt.value = '';
    if(this.mode == 'popup') {//hide the calendar if in popup mode
      this.hide();
    }
  }
    
  if(returnToDefaultMonth == true) {
    this.goToMonth(this.displayYearInitial,this.displayMonthInitial);
  }
  else {
    this.reDraw();
  }
};
//-----------------------------------------------------------------------------
Epoch.prototype.createDayHeading = function ()  //PRIVATE: creates the heading containing the day names
{
  //create the table element
  this.calHeading = document.createElement('table');
  this.calHeading.setAttribute('id',this.name+'_caldayheading');
  this.setClass(this.calHeading,'caldayheading');
  var tbody,tr,td;
  tbody = document.createElement('tbody');
  tr = document.createElement('tr');
  this.cols = new Array(false,false,false,false,false,false,false);
  
  //if we're showing the week headings, create an empty <td> for filler
  if(this.showWeeks)
  {
    td = document.createElement('td');
    td.setAttribute('class','wkhead');
    td.setAttribute('className','wkhead'); //<iehack>
    tr.appendChild(td);
  }
  //populate the day titles
  for(var dow=0;dow<7;dow++)
  {
    td = document.createElement('td');
    td.appendChild(document.createTextNode(this.daynames[dow]));
    if(this.selectMultiple) { //if selectMultiple is true, assign the cell a CalHeading Object to handle all events
      td.headObj = new CalHeading(this,td,(dow + this.startDay < ? dow + this.startDay : dow + this.startDay - 7));
    }
    tr.appendChild(td);
  }
  tbody.appendChild(tr);
  this.calHeading.appendChild(tbody);
  return this.calHeading;  
};
//-----------------------------------------------------------------------------
Epoch.prototype.createCalCells = function ()  //PRIVATE: creates the table containing the calendar day cells
{
  this.rows = new Array(false,false,false,false,false,false);
  this.cells = new Array();
  var row = -1, totalCells = (this.showWeeks ? 48 42);
  var beginDate = new Date(this.displayYear,this.displayMonth,1);
  var endDate = new Date(this.displayYear,this.displayMonth,this.monthDayCount[this.displayMonth]);
  var sdt = new Date(beginDate);
  sdt.setDate(sdt.getDate() (this.startDay - beginDate.getDay()) (this.startDay - beginDate.getDay() 0) );
  //create the table element
  this.calCells = document.createElement('table');
  this.calCells.setAttribute('id',this.name+'_calcells');
  this.setClass(this.calCells,'calcells');
  var tbody,tr,td;
  tbody = document.createElement('tbody');
  for(var i=0;i<totalCells;i++)
  {
    if(this.showWeeks//if we are showing the week headings
    {
      if(i % == 0)
      {
        row++;
        tr = document.createElement('tr');
        td = document.createElement('td');
        if(this.selectMultiple) { //if selectMultiple is enabled, create the associated weekObj objects
          td.weekObj = new WeekHeading(this,td,sdt.getWeek(),row)
        }
        else //otherwise just set the class of the td for consistent look
        {
          td.setAttribute('class','wkhead');
          td.setAttribute('className','wkhead'); //<iehack>
        }
        td.appendChild(document.createTextNode(sdt.getWeek()));      
        tr.appendChild(td);
        i++;
      }
    }
    else if(i % == 0//otherwise, new row every 7 cells
    {
      row++;
      tr = document.createElement('tr');
    }
    //create the day cells
    td = document.createElement('td');
    td.appendChild(document.createTextNode(sdt.getDate()));// +' ' +sdt.getUeDay()));
    var cell = new CalCell(this,td,sdt,row);
    this.cells.push(cell);
    td.cellObj = cell;
    sdt.setDate(sdt.getDate() 1)//increment the date
    tr.appendChild(td);
    tbody.appendChild(tr);
  }
  this.calCells.appendChild(tbody);
  this.reDraw();
  return this.calCells;
};
//-----------------------------------------------------------------------------
Epoch.prototype.reDraw = function () //PRIVATE: reapplies all the CSS classes for the calendar cells, usually called after chaning their state
{
  this.state = 1;
  var i,j;
  for(i=0;i<this.cells.length;i++) {
    this.cells[i].selected = false;
  }
  for(i=0;i<this.cells.length;i++)
  {
    for(j=0;j<this.selectedDates.length;j++) { //if the cell's date is in the selectedDates array, set its selected property to true
      if(this.cells[i].date.getUeDay() == this.selectedDates[j].getUeDay() ) {
        this.cells[i].selected = true;
      }
    }

    this.cells[i].setClass();
  }
  //alert(this.selectedDates);
  this.state = 2;
};
//-----------------------------------------------------------------------------
Epoch.prototype.deleteCells = function () //PRIVATE: removes the calendar cells from the DOM (does not delete the cell objects associated with them
{
  this.calCellContainer.removeChild(this.calCellContainer.firstChild)//get a handle on the cell table (optional - for less indirection)
  this.cells = new Array()//reset the cells array
};
//-----------------------------------------------------------------------------
Epoch.prototype.goToMonth = function (year,month//PUBLIC: sets the calendar to display the requested month/year
{
  this.monthSelect.value = this.displayMonth = month;
  this.yearSelect.value = this.displayYear = year;
  this.deleteCells();
  this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
Epoch.prototype.nextMonth = function () //PUBLIC: go to the next month.  if the month is december, go to january of the next year
{
  
  //increment the month/year values, provided they're within the min/max ranges
  if(this.monthSelect.value < 11) {
    this.monthSelect.value++;
  }
  else
  {
    if(this.yearSelect.value < this.rangeYearUpper)
    {
      this.monthSelect.value = 0;
      this.yearSelect.value++;
    }
    else {
      alert(this.maxrange_caption);
    }
  }
  //assign the currently displaying month/year values
  this.displayMonth = this.monthSelect.value;
  this.displayYear = this.yearSelect.value;
  
  //and refresh the calendar for the new month/year
  this.deleteCells();
  this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
Epoch.prototype.prevMonth = function () //PUBLIC: go to the previous month.  if the month is january, go to december of the previous year
{
  //increment the month/year values, provided they're within the min/max ranges
  if(this.monthSelect.value > 0)
    this.monthSelect.value--;
  else
  {
    if(this.yearSelect.value > this.rangeYearLower)
    {
      this.monthSelect.value = 11;
      this.yearSelect.value--;
    }
    else {
      alert(this.maxrange_caption);
    }
  }
  
  //assign the currently displaying month/year values
  this.displayMonth = this.monthSelect.value;
  this.displayYear = this.yearSelect.value;
  
  //and refresh the calendar for the new month/year
  this.deleteCells();
  this.calCellContainer.appendChild(this.createCalCells());
};
//-----------------------------------------------------------------------------
Epoch.prototype.addZero = function (vNumber//PRIVATE: pads a 2 digit number with a leading zero
{
  return ((vNumber < 10'0' '') + vNumber;
};
//-----------------------------------------------------------------------------
Epoch.prototype.addDates = function (dates,redraw