欢迎访问中国最大的EXTJS讨论社区 首页 论坛 教程

Ext教程-一起Ext

当前位置: 首页 > 教程 > Javascript >

Javascript框架的自定义事件

时间:2009-04-09 16:36来源: 作者: 点击:
Javascript框架的自定义事件,很多 Javascript 框架都提供了自定义事件(custom events),例如 jQuery、YUI 以及 Dojo 都支持“document ready”事件。而部分自定义事件是源自回调(callback)。

  

Dean Edwards 最近有篇文章很精彩,忍不住在这里翻译下。

-- Split --

很多 Javascript 框架都提供了自定义事件(custom events),例如 jQuery、YUI 以及 Dojo 都支持“document ready”事件。而部分自定义事件是源自回调(callback)。

回调将多个事件句柄存储在数组中,当满足触发条件时,回调系统则会从数组中获取对应的句柄并执行。那么,这会有什么陷阱呢?在回答这个问题之前,我们先看下代码。

下面是两段代码依次绑定到 DOMContentLoaded 事件中

document.addEventListener("DOMContentLoaded", function() {
  console.log("Init: 1");
  DOES_NOT_EXIST++; // 这里会抛出异常
}, false);

document.addEventListener("DOMContentLoaded", function() {
  console.log("Init: 2");
}, false);

那么运行这段代码会返回什么信息?显然,会看见这些(或者类似的):

Init: 1
Error: DOES_NOT_EXIST is not defined
Init: 2

可以看出,两段函数都被执行。即使第一个函数抛出了个异常,但并不影响第二段代码运行。

麻烦

OK,我们回来看下常见框架中的回调系统。首先,我们看下 jQuery 的(因为它很流行):

$(document).ready(function() {
  console.log("Init: 1");
  DOES_NOT_EXIST++; // 这里会抛出异常
});

$(document).ready(function() {
  console.log("Init: 2");
});

然后控制台中输出了什么?

Init: 1
Error: DOES_NOT_EXIST is not defined

这样问题就很明了了。回调系统其实很脆弱 -- 如果中间有段代码抛出了异常,那么其余将不会被执行。想象下在实际情况中,这后果可能会更严重,譬如有些糟糕的插件可能会“一粒老屎坏了一锅粥”。

其他的框架,Dojo 的情况和 jQuery 类似,不过 YUI 的情况有些许不同。在它的回调系统中,使用了 try/catch 语句避免因异常发生的中断。但有个小小的负面影响,就是看不到相应的异常了。

YAHOO.util.Event.onDOMReady(function() {
  console.log("Init: 1");
  DOES_NOT_EXIST++; // 这里会抛出异常
});

YAHOO.util.Event.onDOMReady(function() {
  console.log("Init: 2");
});

输出:

Init: 1
Init: 2

那么,有无完美的解决方案呢?

解决方案

我想到了个解决方案,就是将回调和事件结合起来。可以先建立个事件,当回调触发时才运行它。由于每个事件都有其独立的运行环境(execution context),那么即使其中某个事件抛出了异常将不会影响其他的回调。

这听起来有点复杂,还是代码说话吧。

var currentHandler;

// 标准事件支持
if (document.addEventListener) {
    document.addEventListener("fakeEvents", function() {
        // 执行回调
        currentHandler();
    }, false);

    // 新建事件
    var dispatchFakeEvent = function() {
        var fakeEvent = document.createEvent("UIEvents");
        fakeEvent.initEvent("fakeEvents", false, false);
        document.dispatchEvent(fakeEvent);
    };
} else {
    // 针对 IE 的代码在后面详细阐述
}

var onLoadHandlers = [];

// 将回调加入数组中
function addOnLoad(handler) {
    onLoadHandlers.push(handler);
};

// 逐条取出回调,并利用上述新建的事件执行
onload = function() {
    for (var i = 0; i < onLoadHandlers.length; i++) {
        currentHandler = onLoadHandlers[i];
        dispatchFakeEvent();
    }
};

万事俱备,让我们将上面坨代码扔到我们新的回调系统中

addOnLoad(function() {
  console.log("Init: 1");
  DOES_NOT_EXIST++; // 这里会抛出异常
});

addOnLoad(function() {
  console.log("Init: 2");
});

上帝保佑,看运行结果我们看到了如下的信息:

Init: 1
Error: DOES_NOT_EXIST is not defined
Init: 2

赞!这就是我们期望的。这两个回调都运行而且互不影响,并且还能获得异常的信息,太好了!

好了,我们回过头来扶起 Internet Explorer 这个“阿斗”(我已经听见场下观众的建议了)。Internet Explorer 不支持 W3C 的标准事件规范,谢天谢地好在它有自身的实现 -- 有个 fireEvents 的方法,但只能在用户事件的时候触发(例如用户点击 click)。

不过终于找到了门道,我们来看下具体代码:

var currentHandler;

if (document.addEventListener) {
    // 省略上述的代码
} else if (document.attachEvent) { // MSIE
    // 利用扩展属性,当此对象被改变时触发
    document.documentElement.fakeEvents = 0;
    document.documentElement.attachEvent("onpropertychange", function(event) {
        if (event.propertyName == "fakeEvents") {
            // 执行回调
            currentHandler();
        }
    });

    dispatchFakeEvent = function(handler) {
        // 触发 propertychange 事件
        document.documentElement.fakeEvents++;
    };
}
声明:本站教程文章版权为一起Ext(http://www.17ext.com/)所有,转载请注明出处
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
推荐内容