在Web开发中,日期选择器是一个常见的需求。然而,大多数日期选择器在弹出新窗口和初始化时耗时较长,这导致用户体验不佳,用户可能会选择手动输入日期,从而增加了出错的可能性。本文将介绍如何利用Microsoft的calendar.htc文件,以更高效的方式实现日历控件。
首先,需要声明命名空间,这是为了确保日历元素有一个唯一的限定符。HTML元素有一个xmlns属性,它声明了astutemedia命名空间。设置这个属性允许在文档中使用astutemedia前缀。接下来,使用import指令将日历元素导入到命名空间中。
<html xmlns:astutemedia>
<head>
<?import namespace="astutemedia" implementation="CalendarPopup.htc">
</head>
import指令是实现主要文档中元素行为的关键。当浏览器开始处理import指令时,它会等待HTC文件的内容完全下载完成后再继续。这种指令的处理方式是行为同步绑定到自定义元素的原因。
使用CalendarPopup.htc元素行为与ASP.NET服务器控件类似,但所有处理都是在客户端完成的。从下面的代码片段中可以看到,Calendar1有一个target属性,它引用了一个文本框,该文本框将被选中的日期填充。第二个元素没有提供target,但通过selectedDate事件属性从onDateSelected事件中获取选定的日期。
<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>
创建行为的解决方案包括四个文件:
Calendar.htc行为有一个小改动,允许双击日期来选择它。这是通过添加一个名为OnCalendarDblClick的自定义事件实现的。
<public:event id="onCalendarDblClick" name="oncalendardblclick">
当日历的内部表格被双击时,这个事件会被调用。引用innerTableElem并将DblClick函数附加到ondblclick事件上。
window.document.all.innerTableElem.attachEvent("ondblclick", DblClick);
DblClick函数...
function DblClick() {
var oEvent = createEventObject();
onCalendarDblClick.fire(oEvent);
}
在Calendar.htm文件中使用这个事件。在OnCalendarDblClick事件上,调用一个名为CloseCalendar的函数,该函数暴露了选定日期的值。
oncalendardblclick="CloseCalendar()"
这个函数是严格必要的,因为可以直接从CalendarPopup.htc调用Unload函数。但添加这个函数可以提高可读性。
function CloseCalendar() {
var val = Calendar.value;
var id = parent.document.body.children[0].ParentId;
parent.parent.document.getElementById(id).Unload(val);
}
CalendarPopup.htc包含了主要功能。它将日历作为自定义元素暴露出来,并以弹出窗口的形式生成日期选择器。首先声明组件。
<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>
从上面的代码示例中可以看到,暴露了一个Target属性,并给它一个默认属性为空字符串。还暴露了一个名为onDateSelected的事件,当弹出窗口卸载时会被调用。Unload方法由Calendar.htm文件中的CloseCalendar函数调用。然后将“Init”函数附加到持有页面(想要使用行为的页面)上的一个事件上,该事件是onContentReady,当持有页面上的内容完全加载时,该事件会被触发。
function Init() {
// Check to see if there is a target element.
if (Target != null && Target != "") {
// Add a double click to the target elem, to show the calendar
winDoc.getElementById(Target).attachEvent("ondblclick", ShowPopup);
}
// Create a popup object
popup = win.createPopup();
popupBody = popup.document.body;
// Add the scriptlet to the popups body.
popupBody.innerHTML = "";
}
Unload函数触发onDateSelected事件,如果存在目标,则用选定的日期值填充它。然后隐藏弹出窗口。
function Unload(val) {
// Create a new event.
var e = createEventObject();
// Expose the selected value with the event.
e.selectedDate = val;
// Fire the event.
onDateSelected.fire(e);
if (Target != null && Target != "") {
// Find the target in the parent document and set the value.
winDoc.getElementById(Target).value = val;
}
// Hide the popup.
popup.hide();
}
最后一个函数是ShowPopup函数;这个函数在日历图标被点击或目标元素被双击时显示弹出窗口。它将弹出窗口定位在点击/双击发生的位置22像素处。它还确保它不会与浏览器窗口的边界冲突。
function ShowPopup() {
var wEvent = win.event;
var winDocBody = winDoc.body;
var popupWidth = 320;
var popupHeight = 250;
// Set the x and y from where the mouse clicks.
x = wEvent.x + 22;
y = wEvent.y - 22;
// Check to see if the popup goes out of bounds.
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";
// Show the popup.
popup.show(40, -80, popupWidth, popupHeight, document.body);
}
<body id="TheBody">
<img src="Calendar.gif" width="16" height="15" style="cursor:hand" onclick="ShowPopup()" title="Click to show calendar" align="absMiddle">
</body>