免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1537 | 回复: 0

绑定DOM事件到对象 [复制链接]

论坛徽章:
0
发表于 2011-12-19 14:02 |显示全部楼层
在上一篇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;
    }
};

})();


您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP