65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义 JavaScript 事件管理器 (CJEM)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (32投票s)

2011年3月6日

GPL3

9分钟阅读

viewsIcon

91236

downloadIcon

3800

一个自定义 JavaScript 事件管理器类,旨在管理网页上的窗口、文档和控件事件

下载源文件

主要文件

控件示例

CJEM 高级控件示例

screenshot.png

引言

这是我多年前,大约在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
参数(标有 * 的是必需的)

* ClassObject (string) - 包含要调用的方法的窗口、文档、类或变量的名称。

* ControlID (string) - 要附加事件的窗口、文档或控件的 ID。

* EventType (string) - 你想要监视的事件类型名称,例如 onmousedownonmouseuponmousemoveonblur 等。

* EventCallBack (function) - 事件发生时要回调的 function()

TotalInstance (integer) - 你希望事件发生的总次数。0 = 完成,null 或 undefined 表示永不结束。

Wait (integer) - 在调用 EventCallBack 方法之前等待的总毫秒数。Null 或 undefined 表示立即执行。

UserObject (object) - 一个自定义参数,用于添加你想要传递给 EventCallBack 的任何内容。

 
Remove (ControlID, EventType, EventCallBack) 从管理器中移除一个窗口、文档或控件事件的监视。这将阻止事件广播到 EventCallBack。
参数(标有 * 的是必需的)

* ControlID (string) - 事件所附加的窗口、文档或控件的 ID。

* EventType (string) - 你想要停止监视的事件类型名称,例如 onmousedownonmouseuponmousemoveonblur 等。

EventCallBack (function) - 用于回调的 function()。如果你指定一个唯一的,它将只移除那一个。Null 或 undefined 将导致所有类似的事件类型被移除。

实现

实现 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 添加两个事件:onmousemoveonmouseout。当你鼠标悬停在 span 上时,它会改变 spaninnerHTML,然后在鼠标移出时将其改回。为了捕获控件事件,文档窗口必须先加载。此外,窗口加载在不同浏览器中可能会触发多次,因此最好的做法是将 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 被点击时,它将触发 DocumentMouseDownHandler1DocumentMouseDownHandler2 方法。巧妙之处在于你也可以像前面的例子一样设置延迟。因此,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 会向你的方法传递一个参数对象。它将包含以下内容:

  1. ClassObject - 包含要调用的方法的窗口、文档、类或变量的名称。
  2. ControlID - 要附加事件的窗口、文档或控件的 ID。
  3. EventType - 事件类型名称,例如 onmousedownonmouseuponmousemoveonblur 等。
  4. TotalInstance - 事件停止处理前剩余的总实例数。
  5. Wait - 在调用你的 EventCallBack 方法之前等待的总毫秒数。
  6. UserObject - 一个自定义参数,用于添加你想要传递给 EventCallBack 的任何内容。
  7. 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

tabcontrol.png

下拉选择控件

dropdown.png

复选框控件

checkbox.png

movableitem.png

结论

我的文章到此结束。如果您发现任何错误或想提出改进建议,请在下方留言。另外请注意,我将所有内容都改成了长命名约定,这可能会导致一些错误。我这样做是为了提高可读性和学习性。希望大家喜欢我的新文章。

历史

  • 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)
© . All rights reserved.