The Problem
Date pickers and calendar popup are a great way for a user to accurately select the date they require. The main problem with most date pickers is that they take an eternity to popup a new window and initialize, this has a negative effect and the user tends to avoid the popup and enter the date manually which may induce errors. In this article, I’ll make use of one of the best calendars on the web today – Microsoft’s calendar.htc and expose it in a more efficient manner.
The Solution
In my last article, I had demonstrated how to use DHTML behaviours to expose common functionality as a CSS style and briefly talked about element behaviours. The Microsoft calendar.htc is an element behaviour that exposes a fully functional DHTML calendar. Microsoft’s calendar is great, and apart from adding a double click event and a bit of styling, I’ve left it intact. Instead of changing the calendar, I decided to create an element behaviour and embed the calendar.htc into it, this way we leave the calendar relatively untouched. The wrapper will expose the calendar in the form of a popup; the popup object is a special type of overlapped window that is very much like a div
/layer
, but unlike the layer
it hides itself when losing the focus and can go over select elements. Here are the main benefits of the control:
- Totally encapsulated date picker / calendar.
- Quick loading.
- Immune to popup blockers (when opening from script).
- RAD.
- Targets HTML controls or ASP.NET web controls.
Requirements
First of all, we need to declare our namespace, this is to ensure that the calendar element has a unique qualifier. The HTML element has a xmlns
attribute that declares the namespace astutemedia
. Setting this attribute allows us to use the prefix astutemedia
in the document. The next step is to import the calendar element into the namespace with the import
directive.
<html xmlns:astutemedia>
<head>
<?import namespace="astutemedia" implementation="CalendarPopup.htc">
</head>
The import directive is really the key to implementing an element behaviour in the primary document. When the browser begins processing the “import
” directive, it waits until the contents of the HTC file have been downloaded completely before continuing. The way this instruction is processed is the reason why the behaviour is bound synchronously to the custom element.
Using the CalendarPopup.htc
Element behaviours are implemented the same as ASP.NET server controls, but all of the processing is done on the client. As you can see from the code snippet below, "Calendar1
" has a target
attribute that references a textbox, which gets populated by the selected date. The second element doesn’t provide a target
but gets the selected date from the onDateSelected
event via the selectedDate
event attribute.
<input class="FormTextBox" id="Date1" type="text"
maxlength="10" name="Date1">
<astutemedia:calendar id="Calendar1" target="Date1"></astutemedia:calendar>
<astutemedia:calendar id="Calendar2"
onDateSelected="alert(window.event.selectedDate)">
</astutemedia:calendar>
Creating the Behaviour
The solution has four files:
- Calendar.gif – This is the calendar icon that gets rendered by the behaviour.
- Calendar.htc – This is Microsoft’s Calendar behaviour.
- Calendar.htm – This is the popup’s body, encapsulated into a HTML file.
- CalendarPopup.htc – This is the main behaviour.
The Calendar.htc behaviour has one minor change that allows us to double click on a date to select it. This is achieved by adding a custom event called OnCalendarDblClick
.
<public:event id="onCalendarDblClick" name="oncalendardblclick">
This gets called when the inner table of the calendar gets double clicked. We reference the innerTableElem
and attach the DblClick
function to the ondblclick
event.
window.document.all.innerTableElem.attachEvent("ondblclick", DblClick);
The function…
function DblClick()
{
var oEvent = createEventObject();
onCalendarDblClick.fire(oEvent);
}
We make use of this event in the Calendar.htm file. On the OnCalendarDblClick
event, we call a function called CloseCalendar
which exposes the value of the selected date.
oncalendardblclick="CloseCalendar()"
This function is strictly necessary, as we could have called Unload
function from the CalendarPopup.htc directly. But adding this function allows readability.
function CloseCalendar()
{
var val = Calendar.value;
var id = parent.document.body.children[0].ParentId;
parent.parent.document.getElementById(id).Unload(val);
}
The CalendarPopup.htc holds the main functionality. It exposes the calendar as a custom element and produces the date picker as a popup. We first start off by declaring the component.
<public:component tagname="Calendar">
<public:defaults viewLinkContent="true" />
<public:property name="Version" value="Calendar 1.0" />
<public:property name="Target" value="" />
<public:event id="onDateSelected" name="ondateselected">
<public:method name="Unload" />
<public:attach event="oncontentready" onevent="Init()" />
</public:component>
As you can see from the above code sample, we expose a Target
property and give it a default property of an empty string. We also expose an event called onDateSelected
, which gets called when the popup unloads. The Unload
method gets called by the CloseCalendar
function in the Calendar.htm file. We then attach the “Init
” function to an event on the holding page (the page where we want to use the behaviour), the event is onContentReady
, this event gets fired when the content on the holding page is fully loaded.
The Init
function first checks to see if the Target
property is set, and if so attaches the onDblClick
event to the element. This will allow the double click of the element to show the calendar. We then create the popup itself and populate it with the Calendar.htm file.
function Init()
{
if(Target != null && Target != "")
{
winDoc.getElementById(Target).attachEvent("ondblclick", ShowPopup);
}
popup = win.createPopup();
popupBody = popup.document.body;
popupBody.innerHTML = "<object id='cal' width='100%'
ParentId='" + id + "' height='100%' type='text/x-scriptlet'
data='Calendar.htm'></object>";
}
The Unload
function fires the onDateSelected
event and if a target exists, populate it with the selected date value. It then hides the popup.
function Unload(val)
{
var e = createEventObject();
e.selectedDate = val;
onDateSelected.fire(e);
if(Target != null && Target != "")
{
winDoc.getElementById(Target).value = val;
}
popup.hide();
}
The last function is the ShowPopup
function; this function shows the popup when the the calendar icon is clicked or the target element is double clicked. It positions the popup 22 pixels from where the click/double-click occurred. It also makes sure that it doesn’t conflict with the boundaries of the browser window.
function ShowPopup()
{
var wEvent = win.event;
var winDocBody = winDoc.body;
var popupWidth = 320;
var popupHeight = 250;
x = wEvent.x + 22;
y = wEvent.y - 22;
if (x + popupWidth > winDocBody.clientWidth)
x = wEvent.x - (popupWidth + winDocBody.scrollLeft + 22);
else
x += winDocBody.scrollLeft;
if (y + popupHeight > winDocBody.offsetHeight)
y = wEvent.y - (popupHeight + winDocBody.scrollTop + 22);
else
y += winDocBody.scrollTop;
popupBody.style.border = "1px solid #333333";
popup.show(40, -80, popupWidth, popupHeight, document.body);
}
Finally, we output our calendar icon and add an onClick
event to show the popup.
<body id="TheBody">
<img src="Calendar.gif" width="16" height="15" style="cursor:hand"
onclick="ShowPopup()" title="Click to show calendar" align="absMiddle">
</body>
Conclusion
This behaviour makes calendars and date pickers a quick and easy solution to a very common problem. Element behaviours are a great way to implement and encapsulate common functionality and are very quick to use. If you are using ASP.NET, you can wrap the element behaviour in a server control to expose the selected date on the server side.
Links and Resources
Downloads and Demos