Chinaunix首页 | 论坛 | 博客
  • 博客访问: 485905
  • 博文数量: 55
  • 博客积分: 1867
  • 博客等级: 上尉
  • 技术积分: 587
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-29 01:33
文章分类

全部博文(55)

文章存档

2013年(1)

2012年(2)

2011年(16)

2010年(11)

2009年(5)

2008年(10)

2007年(8)

2006年(2)

分类: 系统运维

2011-01-06 01:30:10

在上一篇Blog 《舍去jQuery,面向M编程,而不是面向V编程》中,在面向对象编程的例子中需要绑定事件到对象的方法上,而不是绑定到全局方法上。在这一篇里,我们可以继续延续这个话题,以完善事件绑定机制。
  我们想要达到的目标是,一个对象已经封装了事件处理的逻辑,而我们知道对象本身是不能捕获事件的,只有DOM元素可以捕获事件,如何动态地绑定DOM事件的处理句柄到对象实例上。看下面的代码:

var Button = function(id){
    this.div = document.createElement("div");
    this.div.id = id;

    this.onClick = function(){
        alert(this.div.id); // 这里的this是div,而不是我们想要的Button实例

    };

    this.div.onclick = this.onClick;

};


代码的意图很明显,div上的onclick由Button对象的onClick方法来处理,但我们会发现,其实这样工作是有问题的,this.onClick方法里的this其实在事件处理阶段是指向div的,而不是我们想当然的Button实例。如何让this指向Button实例呢?我们提到一个技巧, 扩展JavaScript的Function原型:

Function.prototype.bind = function(context){
    var _method = this;
    return function(e) {
        var array = [e || window.event];
        return _method.apply(context, array);
    };
};


为一个方法绑定执行的上下文,这样就为将this指定到Button实例提供了可能,上面Button的例子可以改为:

Function.prototype.bind = function(context){
    var _method = this;
    return function(e) {
        var array = [e || window.event];
        return _method.apply(context, array);
    };
};

var Button = function(id){
    this.div = document.createElement("div");
    this.div.id = id;

    this.onClick = function(e){
        alert(this.div.id); // 这时this是指向Button实例的

    };

    this.div.onclick = this.onClick.bind(this);
};


这样虽然可以工作了,但还不完美,如果我们需要在处理事件时有多个处理句柄,并且还能随时移除事件句柄,那上面的方法还需要改进,就是加入attachEvent和detachEvent了。

Function.prototype.bind = function(context){
    var _method = this;
    return function(e) {
        var array = [e || window.event];
        return _method.apply(context, array);
    };
};
/**
 * Attach event listener to a DOM object
 *
 * @param domObj
 * @param evType, for example "click", "load" etc
 * @param thi$, the context of handler
 * @param handler, event handler
 */

$attachEvent = function(domObj, evType, thi$, handler){
    var _handler = handler.bind(thi$);

    if(domObj.addEventListener){
        domObj.addEventListener(evType, _handler, false);
    }else{
        domObj.attachEvent("on"+evType, _handler);
    }

    return _handler;
};

/**
 * Detach event listener from a DOM object
 *
 * @see J$VM.attachEvent(domObj, evType, thi$, handler)
 */

$detachEvent = function(domObj, evType, thi$, handler){
    if(domObj.removeEventListener){
        domObj.removeEventListener(evType, handler, false);
    }else{
        domObj.detachEvent("on"+evType, handler);
    }
};
var Button = function(id){
    this.div = document.createElement("div");
    this.div.id = id;

    this.onClick = function(e){
        alert(this.div.id); // 这时this是指向Button实例的


        // 假设在这个逻辑中我们需要移除事件句柄,但其实这一句也是不如我们预期的。

       $detachEvent(this.div, "click", this, this.onClick);
    };

    $attachEvent(this.div, "click", this, this.onClick);
};


引入这两个方法后,再看Button的例子,好像完美了,但实际上,detachEvent(this.div, "click", this, this.onClick)这一句并不如我们预期的那样工作,因为,在attachEvent内部,我们attach的实际上是bind方法返回的方法,而不是this.onClick,所以detachEvent时,并不能找到这个this.onClick。我们还需要继续改造bind方法和detachEvent方法。

Function.prototype.bind = function(context){
    var _method = this;
    _method.__handler__ = function(e) {
        // 在_method里暗藏一个真实的绑定句柄

        var array = [e || window.event];
        return _method.apply(context, array);
    };
    return _method.__handler__;
};

$detachEvent = function(domObj, evType, thi$, handler){
    var _handler = handler.__handler__ || handler; // 取出__handler__


    if(domObj.removeEventListener){
        domObj.removeEventListener(evType, _handler, false);
    }else{
        domObj.detachEvent("on"+evType, _handler);
    }
};


到这里,我们的事件绑定机制基本上完美了,最后附上一段完整的代码:

(function(){
window.J$VM = {};

Function.prototype.bind = function(context){
    var _method = this;
    _method.__handler__ = function(e) {
        var array = [e || window.event];
        return _method.apply(context, array);
    };
    return _method.__handler__;
};

/**
 * Attach event listener to a DOM object
 *
 * @param domObj
 * @param evType, for example "click", "load" etc
 * @param thi$, the context of handler
 * @param handler, event handler
 */

J$VM.attachEvent = function(domObj, evType, thi$, handler){
    var _handler = handler.bind(thisObj);

    if(domObj.addEventListener){
        domObj.addEventListener(evType, _handler, false);
    }else{
        domObj.attachEvent("on"+evType, _handler);
    }

    return _handler;
};

/**
 * Detach event listener from a DOM object
 *
 * @see J$VM.attachEvent(domObj, evType, thi$, handler)
 */

J$VM.detachEvent = function(domObj, evType, thi$, handler){
    var _handler = handler.__handler__ || handler;

    if(domObj.removeEventListener){
        domObj.removeEventListener(evType, _handler, false);
    }else{
        domObj.detachEvent("on"+evType, _handler);
    }
};

/**
 * Cancel event bubble
 */

J$VM.cancelBubble = function(e){
    if(e.stopPropagation){
        e.stopPropagation();
    }else{
        window.event.cancelBubble = true;
    }
};

})();


阅读(4190) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~