自定义 JavaScript 事件管理器 (CJEM)
一个自定义 JavaScript 事件管理器类,旨在管理网页上的窗口、文档和控件事件
下载源文件
主要文件
控件示例
- 下载日期日历文本框示例 - 27.18 KB
- 下载选项卡控件示例 - 2.33 KB
- 下载下拉选择控件 - 2.38 KB
- 下载复选框示例 - 4.73 KB
- 下载可移动项目示例 - 10.54 KB
- 下载阻止默认行为示例 - 2.03 KB
CJEM 高级控件示例

引言
这是我多年前,大约在2002年开发的一个脚本。它是一个 JavaScript 事件管理器,解决了浏览器大战造成的许多问题,并使在网页上开发带事件的控件变得有趣和容易。我无法为它想出一个吸引人的名字,所以我称它为 (CJEM) 自定义 JavaScript 事件管理器。我决定将其公开发布,因为 JavaScript 的开发量尤其在过去两年中不断增加。也许有人会发现这篇文章对他们的 Web 开发有益。希望大家喜欢。
我为什么创建它?
我厌倦了使用浏览器事件管理器,结果却发现我的代码在不同版本和浏览器中停止工作。这些管理器也问题很多,并且没有实现在线文档中报告的所有内容。浏览器公司在 JavaScript、HTML、CSS 和 DOM 对象之间缺乏命名约定,导致许多开发人员在编写跨兼容代码时感到抓狂。坚持标准命名结构就那么难吗?
你为什么要使用它?
这是一个非常轻量、简单、高度组织和深思熟虑的脚本,已经在生产中使用了多年。它在用 JavaScript 和 DOM 创建网页控件事件时,让生活变得轻松。我还尝试遵循在线标准文档,并创建了缺失或不统一的部分。使用这个脚本可以使你的 JavaScript 源文件更有条理。该脚本也适用于大多数(如果不是所有)浏览器和版本,如 Internet Explorer 5+、Firefox、Chrome、Opera、Safari、Konqueror 和 Netscape(如果你敢的话)。
类属性和方法
标题: 自定义 JavaScript 事件管理器 (CJEM) - 类 | ||
命名空间:全局 | ||
描述:用于创建窗口、文档和控件事件的主要事件管理器类。 | ||
属性 | 描述 | 返回值 |
事件 |
此属性变量包含事件、类名、控件ID、事件类型、回调和其他自定义设置的数组集合。此属性主要用于事件管理器的内部信息,但已开放以允许开发人员对其进行扩展。 | 数组集合 |
方法 | 描述 | 返回值 |
Add (ClassObject, ControlID, EventType, EventCallBack, TotalInstance, Wait, UserObject) |
向管理器添加一个窗口、文档或控件事件以进行监视。当事件触发时,管理器将调用事件回调方法。Add 方法还支持通过再次使用该方法来添加多个 EventCallBack 。 |
无 |
参数(标有 * 的是必需的) * * * *
|
||
Remove (ControlID, EventType, EventCallBack) |
从管理器中移除一个窗口、文档或控件事件的监视。这将阻止事件广播到 EventCallBack。 | 无 |
参数(标有 * 的是必需的) * *
|
实现
实现 CJEM 相当容易,只需在网页头部包含外部 'cjem.js
' 文件即可。然后使用 CJEM 类的 add 或 remove 方法来创建或移除事件处理程序。CJEM 允许 JS 与 HTML 完全分离。不再需要将内联 JS 添加到 HTML 标签来创建 DOM 事件。一切都在 JS 文件中处理得很好。
示例 1 - 捕获文档事件
第一个示例捕获文档的 onmousedown
事件。相对简单的示例,但不到 10 秒即可创建。当你点击文档的任何位置时,它会显示一个 alert
消息,告诉你发生了什么。
// Captured the document mouse down event.
function DocumentMouseDownHandler(src)
{
alert("You captured the '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Capture the document mouse down
$CJEM.Add('document','document', 'onmousedown', DocumentMouseDownHandler);
示例 2 - 捕获窗口事件 + 控件事件
此示例只捕获一次窗口加载事件。然后它向 span
添加两个事件:onmousemove
和 onmouseout
。当你鼠标悬停在 span
上时,它会改变 span
的 innerHTML
,然后在鼠标移出时将其改回。为了捕获控件事件,文档窗口必须先加载。此外,窗口加载在不同浏览器中可能会触发多次,因此最好的做法是将 TotalInstance
设置为 1
。这确保它只触发一次。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Capture the document example mouse move.
$CJEM.Add('document','example', 'onmousemove', DocumentExampleMouseMoveHandler);
// Capture the document example mouse move.
$CJEM.Add('document','example', 'onmouseout', DocumentExampleMouseOutHandler);
}
// Captured the document example mouse move event.
function DocumentExampleMouseMoveHandler(src)
{
$O(src.ControlID).innerHTML = 'Mouse Over This! - Mouse is Over';
}
// Captured the document example mouse out event.
function DocumentExampleMouseOutHandler(src)
{
$O(src.ControlID).innerHTML = 'Mouse Over This!';
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
示例 3 - 事件管理器总实例数
我觉得需要在 add 方法中添加一个总实例参数。你可能会发现有必要使用它,就像之前的窗口加载方法一样。在这个例子中,我们将创建一个 span
,它将只 onmousedown
3 次,然后停止。它将显示一个 alert
并告诉你还剩下多少实例。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Capture the document example mouse move.
$CJEM.Add('document','example', 'onmousedown', DocumentExampleMouseDownHandler, 3);
}
// Captured the document example mouse down event.
function DocumentExampleMouseDownHandler(src)
{
alert("You captured the '" + src.EventType + "' from the control: '" +
src.ControlID + "'. Total Instances Left: '" + src.TotalInstance + "'");
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
示例 4 - 事件管理器等待时间
我还觉得需要在 add
方法中添加一个 wait
参数。wait
参数允许你指定一个以毫秒为单位的整数,表示在事件触发 EventCallBack
方法之前等待的时间。这使你可以自由创建一些独特的控件,即使事件已经发生,它们也会稍后触发。通过下面的示例,你可以将 textbox
设置为多少毫秒,然后单击 span
进行等待。请记住 1000 毫秒等于 1 秒。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Get milliseconds to wait
var msec = $O('mseconds').value;
// Capture the document example mouse down.
$CJEM.Add('document','example', 'onmousedown',
DocumentExampleMouseDownHandler, null, msec);
}
// Captured the document example mouse down event.
function DocumentExampleMouseDownHandler(src)
{
alert("You captured the '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
示例 5 - 附加更多 EventCallBacks
你还可以向同一个事件附加多个 EventCallBacks
。这允许处理程序在一个事件上调用单独的方法,例如如下所示。当 span
被点击时,它将触发 DocumentMouseDownHandler1
和 DocumentMouseDownHandler2
方法。巧妙之处在于你也可以像前面的例子一样设置延迟。因此,DocumentMouseDownHandler2
将在第一个 alert 之后 2 秒触发。这提供了强大的功能,尤其是在 AJAX 中。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Capture the document example mouse down.
$CJEM.Add('document','example', 'onmousedown', DocumentMouseDownHandler1);
// Capture the document example mouse down.
$CJEM.Add('document','example', 'onmousedown', DocumentMouseDownHandler2, null, 2000);
}
// Captured the document mouse down event.
function DocumentMouseDownHandler1(src)
{
alert("You captured the first '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Captured the document mouse down event.
function DocumentMouseDownHandler2(src)
{
alert("You captured the second '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
示例 6 - 事件冒泡
当事件触发时,它会通过所有父容器冒泡。这是大多数主流浏览器实现的一个很棒的功能。问题是浏览器有不同的处理方式。处理作用域和方法命名约定以进行取消可能是一个真正的痛苦。因此我决定修复这些问题,使其变得简单易用,就像它本应做到的那样。要停止事件冒泡,只需在你的 EventCallBack
方法中“return true
”。如果你希望事件冒泡发生,只需 return false
或不添加返回。这不仅使停止冒泡变得容易,还解决了作用域问题。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Capture the document mouse down.
$CJEM.Add('document','document', 'onmousedown', DocumentMouseDownHandler);
// Capture the span1 mouse down with bubbling.
$CJEM.Add('document','span1', 'onmousedown', Span1MouseDownHandler);
// Capture the span2 mouse down without bubbling.
$CJEM.Add('document','span2', 'onmousedown', Span2MouseDownHandler);
}
// Captured the document mouse down event.
function DocumentMouseDownHandler(src)
{
alert("You captured the '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Captured the span1 mouse down event and bubble to document.
function Span1MouseDownHandler(src)
{
alert("You captured the '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
}
// Captured the span2 mouse down event and stop bubbling.
function Span2MouseDownHandler(src)
{
alert("You captured the '" + src.EventType +
"' from the control: '" + src.ControlID + "'");
// Cause the event to stop bubbling.
return true;
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
创建您的 EventCallBack 方法
创建 EventCallBack
方法后,CJEM 会向你的方法传递一个参数对象。它将包含以下内容:
ClassObject
- 包含要调用的方法的窗口、文档、类或变量的名称。ControlID
- 要附加事件的窗口、文档或控件的 ID。EventType
- 事件类型名称,例如onmousedown
、onmouseup
、onmousemove
、onblur
等。TotalInstance
- 事件停止处理前剩余的总实例数。Wait
- 在调用你的EventCallBack
方法之前等待的总毫秒数。UserObject
- 一个自定义参数,用于添加你想要传递给EventCallBack
的任何内容。event
- 这是窗口事件对象。
上面的参数对象将为你提供大量信息,帮助你创建出色的控件。作用域不再是访问类名、控件 ID、父 ID、事件类型、窗口事件等信息的难题。
阻止默认事件行为
有人要求我展示一个如何阻止元素默认行为的示例。将下面的代码添加到回调处理程序将阻止事件触发其默认行为。示例显示了一个标准链接元素,它在单击或鼠标按下时不起作用,只在双击时起作用。使用下面的代码将使其能够工作。
// Captured the Window Load Handler.
function WindowLoadHandler(src)
{
// Setup events
$CJEM.Add('document','link', 'onmousedown', mousedownhandler);
$CJEM.Add('document','link', 'onclick', mousedownhandler);
$CJEM.Add('document','link', 'ondblclick', doubleclickhandler);
}
// Handles the link mouse down and click.
function mousedownhandler(src)
{
if(src.event.preventDefault){ src.event.preventDefault()}
else{src.event.stop()};
src.event.returnValue = false;
src.event.stopPropagation();
return true;
}
// Handles the link double click.
function doubleclickhandler(src)
{
window.location = $O(src.ControlID).href;
}
// Capture the Window Load Event one time.
$CJEM.Add('window','window','onload', WindowLoadHandler, 1);
自定义 JavaScript 事件管理器 - 源代码
以下是自定义 JavaScript 事件管理器 (CJEM) 的源代码。我稍后还会上传一个压缩版本用于网络。
// Registers a Namespace.
var $N={R:function(N){var o=window;var x=false;
for(var a=N.split('.');a.length>0;){var s=a.shift();
if(a.length==0){if(o[s]){x=true;}}if(!o[s]){o[s]={};}o=o[s];}if(x){return 1;}}};
// Finds an object by id.
function $O(S){return document.getElementById(S);}
// Check if object is undefined, null or string is empty.
function $NU(O){if(O==undefined||O==null){return true;}
else{if(typeof(O)=='string'&&O==''){return true;}else{return false;}}}
// Custom Javascript Event Manager.
var $CJEM = {
// The events array collection.
Events: null,
// Add an event to the manager.
Add: function(ClassObject, ControlID, EventType,
EventCallBack, TotalInstance, Wait, UserObject) {
// Check if events collection is null and create it or reuse it.
if ($NU(this.Events)) { this.Events = new Array(); }
// Check if that id exists already as object or create it.
if ($NU(this.Events[ControlID])) { this.Events[ControlID] = {}; }
// Get the event item for the specific control id.
var EventItem = this.Events[ControlID];
// Check if handlers exist or create new array for holding multiple same events.
if ($NU(EventItem[EventType])) { EventItem[EventType] = new Array(); }
// Get the handlers for the event item.
var Handlers = EventItem[EventType];
// Add new event handler.
Handlers.push({'ClassObject': ClassObject, 'ControlID': ControlID,
'EventType': EventType, 'EventCallBack': EventCallBack,
'TotalInstance': TotalInstance, 'Wait': Wait, 'UserObject':
UserObject, event: null});
// Register the event with the manager.
$CJEM.Register(ControlID, EventType);
},
// Remove an event from the manager.
Remove: function(ControlID, EventType, EventCallBack) {
// Check if collection contains events.
if ($NU(this.Events)) { return; }
// Get the Event by control id.
var EventItem = this.Events[ControlID];
// Check if Event Item has handlers.
if ($NU(EventItem)) { return; }
// Get the handlers for the event item.
var Handlers = EventItem[EventType];
// Check if event item contains handlers.
if ($NU(Handlers)) { return; }
if(!$NU(EventCallBack))
{
// Find specific handler with eventcallback.
for (var i = 0; i < Handlers.length; i++) {
if (EventCallBack == Handlers[i].EventCallBack)
{ Handlers[i] = null; Handlers.sort(); Handlers.pop(); }
}
}
else
{
// Remove all event handlers.
Handlers = [];
}
// Reset if no more handlers.
if(Handlers.length==0)
{
Handlers = null;
EventItem[EventType] = null;
delete EventItem[EventType];
// UnRegister from method handlers.
$CJEM.UnRegister(ControlID, EventType);
}
},
// Unregister event in the manager.
UnRegister: function(ControlID, EventType) {
// Check for window, document or control event to unregister.
switch (ControlID) {
case 'window': window[EventType] = null; break;
case 'document': document[EventType] = null; break;
default:
var control = $O(ControlID);
if($NU(control)){ return; }
control[EventType] = null; break;
}
},
// Register event in the manager.
Register: function(ControlID, EventType) {
// Check for window, document or control event to register.
switch (ControlID) {
case 'window': window[EventType] = function(e)
{ $CJEM.Caller(e, ControlID, EventType); }; break;
case 'document': document[EventType] =
function(e) { $CJEM.Caller(e, ControlID, EventType); }; break;
default:
var control = $O(ControlID);
if($NU(control)){ return; }
control[EventType] = function(e)
{ $CJEM.Caller(e, ControlID, EventType); }; break;
}
},
// Calls the EventCallBack for each handler.
Caller: function(e, ControlID, EventType) {
// Get the event object from the window.
if (!e) { e = window.event; }
// Get the event item from the collection.
var EventItem = this.Events[ControlID];
// Get the event item handlers.
var Handlers = EventItem[EventType];
if($NU(Handlers)){ return; }
for (var i = 0; i < Handlers.length; i++) {
// Get the EventObject.
var EventObject = Handlers[i];
// Set the window event into the event object.
EventObject.event = e;
// Check for TotalInstance.
if(!$NU(EventObject.TotalInstance))
{
if(EventObject.TotalInstance>0)
{
EventObject.TotalInstance -= 1;
}
}
// Output object
var handlerobject = {'ClassObject': Handlers[i].ClassObject,
'ControlID': Handlers[i].ControlID, 'EventType': Handlers[i].EventType,
'TotalInstance': Handlers[i].TotalInstance,
'Wait': Handlers[i].Wait, 'UserObject':
Handlers[i].UserObject, event: e};
// Check for Wait Timer
if(!$NU(EventObject.Wait))
{
// Execute callback on wait timer.
var t=setTimeout(function(){ if(EventObject.EventCallBack(handlerobject))
{ if (navigator.appVersion.indexOf('MSIE') != -1)
{ e.cancelBubble = true; } else { e.stopPropagation(); }}},
EventObject.Wait);
}
else
{
// No wait just fire.
if (EventObject.EventCallBack(handlerobject)) {
// Execute callback handler for event.
if (navigator.appVersion.indexOf('MSIE') != -1)
{ e.cancelBubble = true; } else { e.stopPropagation(); }
}
}
// Remove event if total instances equals 0.
if(EventObject.TotalInstance==0)
{
$CJEM.Remove(ControlID, EventType, EventObject.EventCallBack);
}
}
}
};
压缩版
我创建了一个 CJEM 的压缩版本。它只有区区 1.9 KB。我删除了注释、长变量名和空格。它不改变输出命名,因此你可以用它替换原始版本。
即,它不会破坏你正在运行的代码。
控件示例
我将在接下来的几个月里向这篇文章添加许多控件示例。此外,如果你有要分享的示例,请在下方添加,如果它足够好,我将把它添加到主文章中,并注明你的贡献。
Tab Control
下拉选择控件
复选框控件
结论
我的文章到此结束。如果您发现任何错误或想提出改进建议,请在下方留言。另外请注意,我将所有内容都改成了长命名约定,这可能会导致一些错误。我这样做是为了提高可读性和学习性。希望大家喜欢我的新文章。
历史
- 2011年8月3日 - 创建自定义
Datagrid
- 2011年7月5日 - 制作新版本
- 2011年6月13日 - 创建一些新控件
- 2011年5月19日 - 应要求提供阻止默认行为的示例
- 2011年5月7日 - 发现多事件和单实例的小问题
- 2011年4月28日 - 创建了可移动项目示例
- 2011年4月20日 - 扩展代码以包含 CSS 类支持
- 2011年4月12日 - 构建更高级的控件示例
- 2011年4月8日 - 创建了复选框示例
- 2011年4月3日 - 创建了下拉选择控件示例
- 2011年3月31日 - 创建了 Tab 控件示例
- 2011年3月30日 - 创建了一些示例控件库
- 2011年3月10日 - 创建了压缩版 CJEM,文件大小 1.9 KB
- 2011年3月9日 - 添加了日期日历文本框示例
- 2011年3月6日 - 发布 CJEM 采用通用公共许可证 (GPL3)