让 Microsoft Calendar.htc DHTML 行为更好?






3.89/5 (6投票s)
Microsoft Calendar.htc 是一个出色的 DHTML 行为,我尝试让它变得更好、更快、更易于使用。
问题
日期选择器和日历弹出窗口是用户准确选择所需日期的绝佳方式。大多数日期选择器的主要问题是它们弹出新窗口和初始化需要很长时间,这会产生负面影响,用户往往会避免使用弹出窗口,而是手动输入日期,这可能会导致错误。在本文中,我将利用当今网络上最好的日历之一——Microsoft 的 *calendar.htc*,并以更高效的方式将其公开。
解决方案
在我 上一篇文章 中,我演示了如何使用 DHTML 行为将常用功能公开为 CSS 样式,并简要介绍了元素行为。Microsoft 的 calendar.htc 是一个元素行为,它公开了一个功能齐全的 DHTML 日历。Microsoft 的日历很棒,除了添加双击事件和一些样式外,我将其保持不变。我没有更改日历,而是决定创建一个元素行为并将 *calendar.htc* 嵌入其中,这样日历就能相对保持不变。包装器将以 弹出窗口 的形式公开日历;弹出窗口对象是一种特殊的重叠窗口,非常类似于 div
/layer
,但与 layer
不同的是,它在失去焦点时会隐藏自己,并且可以覆盖 select 元素。该控件的主要优点是:
- 完全封装的日期选择器/日历。
- 加载速度快。
- 免疫弹出窗口阻止程序(从脚本打开时)。
- RAD(快速应用程序开发)。
- 目标为 HTML 控件或 ASP.NET Web 控件。
要求
首先,我们需要声明我们的命名空间,这是为了确保日历元素具有唯一的限定符。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
,而是通过 onDateSelected
事件通过 selectedDate
事件属性获取选定的日期。
<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.gif - 这是行为渲染的日历图标。
- Calendar.htc - 这是 Microsoft 的日历行为。
- Calendar.htm - 这是封装到 HTML 文件中的弹出窗口体。
- CalendarPopup.htc - 这是主要行为。
Calendar.htc 行为有一个小改动,允许我们双击日期进行选择。这是通过添加一个名为 OnCalendarDblClick
的自定义事件来实现的。
<public:event id="onCalendarDblClick" name="oncalendardblclick">
当内部表格被双击时,会调用此函数。我们引用 innerTableElem
并将 DblClick
函数附加到 ondblclick
事件。
window.document.all.innerTableElem.attachEvent("ondblclick", 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
,当宿主页面上的内容完全加载时,会触发此事件。
Init
函数首先检查 Target
属性是否已设置,如果已设置,则将 onDblClick
事件附加到该元素。这将允许双击该元素来显示日历。然后,我们创建弹出窗口本身,并使用 Calendar.htm 文件填充它。
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 = "<object id='cal' width='100%'
ParentId='" + id + "' height='100%' type='text/x-scriptlet'
data='Calendar.htm'></object>";
}
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);
}
最后,我们输出日历图标并添加一个 onClick
事件来显示弹出窗口。
<body id="TheBody">
<img src="Calendar.gif" width="16" height="15" style="cursor:hand"
onclick="ShowPopup()" title="Click to show calendar" align="absMiddle">
</body>
结论
这种行为使日历和日期选择器成为解决非常普遍问题的快速简便的解决方案。元素行为是实现和封装常用功能的绝佳方式,并且使用起来非常快速。如果您使用的是 ASP.NET,可以将元素行为封装在服务器控件中,以便在服务器端公开选定的日期。