January 15th, 2009
I am one of those rare developers who really doesn't mind having to work with Javascript. In fact, with all the cool libraries available to developers nowadays, cross-browser issues that previously made working with Javascript a chore have all but been eliminated. My favourite Javascript library happens to be Jquery. I find Jquery's syntax and methods easy to work with and its structure to be logical and well thought out - not to mention Jquery's large community support base.
Anyway, I had a client who was thinking of revamping their online registration system and they wanted some of those fancy little popup calendars for date picking. Jquery has a great plug-in called 'Datepicker' which is quite handy for this sort of thing but I decide to create my own just for fun. Sometimes I code just for the sake of coding - I mean, it's fun to know how things work right?
If you would like to download the example, you can do so here or, to see the calendar in operation, click on the image below:
The part that I was most curious about was how I was going to account for the leap years algorithmically (And no, it's not as easy as checking for divisibility by four - to understand why, check this out).
Anyway, Google brought me to the Wikipedia entry for 'Leap year' where I found this bit of 'psuedocode':
function isLeapYear (year):
if ((year modulo 4 is 0) and (year modulo 100 is not 0)) or (year modulo 400 is 0)
then true
else false
Which, in Javascript, reads like this (where 'dateArray[1]' is the month of Febuary):
if (((year % 4 == 0) & (year % 100 != 0)) || (year % 400 == 0)) {
dateArray[1] = 28;
} else {
dateArray[1] = 29;
}
And here's the main script:
// Arrays
var monthArray = ['January','Febuary','March','April','May','June','July','August','September','October','November','December'];
var dateArray = [31,29,31,30,31,30,31,31,30,31,30,31];
var dayArray = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
// Current date
var d = new Date();
var day = d.getDate();
var month = d.getMonth();
var year = d.getFullYear();
// Utility - used to calculate day of week first day of month falls upon
var dx = new Date();
// Toggle vars
var calendar;
var style;
// Onload handler - function executes once page has been loaded
function onloadHandler() {
getCurrentDate();
// Get style
calendar = document.getElementById("calendar");
if (calendar.currentStyle) {
style = calendar.currentStyle["visibility"];
} else if (window.getComputedStyle) {
style = document.defaultView.getComputedStyle(calendar,null).getPropertyValue("visibility");
}
}
// Toggle visibility - make calendar visible when input is 'clicked'
function toggleVisibility() {
calendar.style.visibility = 'visible';
}
// Get current date - change input value to current date on page load
function getCurrentDate() {
var output = document.getElementById("formatted-date");
output.value = monthArray[month] + ' ' + day + ', ' + year;
}
// Get date - get the choosen date when calendar day 'clicked' on
function getDate(dayOfMonth) {
var output = document.getElementById("formatted-date");
output.value = monthArray[month] + ' ' + dayOfMonth.innerHTML + ', ' + year;
calendar.style.visibility = 'hidden';
}
// Change month - decrement/increment month
function changeMonth(movement) {
var theMonth = document.getElementById("theMonth");
var theYear = document.getElementById("theYear");
// Set back a month
if (movement == 'back') {
if (month == 0) {
var back = month = 11;
year -= 1;
theYear.innerHTML = year;
} else {
var back = month -= 1;
}
}
// Set forward a month
if (movement == 'forward') {
if (month == 11) {
month = 0;
var forward = monthArray[month];
year += 1;
theYear.innerHTML = year;
} else {
month += 1;
var forward = monthArray[month];
}
}
// Change month
theMonth.innerHTML = month;
addCalendar()
}
// Add calendar
function addCalendar() {
// Establish day of week that first day falls upon
dx.setFullYear(year,month,1);
var xCount = dx.getDay();
// Account for leap years
if (((year % 4 == 0) & (year % 100 != 0)) || (year % 400 == 0)) {
dateArray[1] = 28;
} else {
dateArray[1] = 29;
}
// Get element for insertion
var theTest = document.getElementById("calendar");
// Add table head
var data = '<table><tr><td class="arrow" onclick="changeMonth('back')"></td><td id="theMonth" class="month-year" colspan="3">';
data += monthArray[month] + '</td><td id="theYear" class="month-year" colspan="2">';
data += year + '</td><td class="arrow" onclick="changeMonth('forward')"></td></tr>';
// Add days of the week
data += '<tr>';
for (var i in dayArray) {
data += '<td>' + dayArray[i] + '</td>';
}
data += '</tr><tr>';
// Position first day of month
if (dx.getDay() != 0) {
data += '<td id="spacer" colspan="' + dx.getDay() + '"></td>';
}
// Add calendar days
for (var i = 1; i <= dateArray[month]; i++) {
xCount += 1;
if (xCount % 7 == 1 && xCount != 1) {
data += '</tr><tr>';
}
data += '<td onclick="getDate(this)">' + i + '</td>';
}
data += '</tr></table>';
// Add data to element
theTest.innerHTML = data;
}
Make sure to download the ZIP if you really want to get a good look at how things work. I should point out that this was coded in the old 'obtrusive' way (blush). This is not the way I would code for production and would suggest that, if you plan on using this script, event handlers be registered programatically rather than inline - in a more 'unobtrusive' manor. Better yet, why not let Jquery do most of the heavy lifting. Jquery, by design, makes for better scripting because it forces you to code in an unobtrusive manor. So, while this script may be instructive, it would be best to do as I say, not as I do.
Enjoy!
1June 9th, 2009 at 2:44 am
Javascript Geek writes:
Nice code example, especially the leap year issues - great find!