js实现自定义事件(字面量 && 原型)

1.前言

我们平时在操作dom时候经常会用到onclick,onmouseover等一系列浏览器特定行为的事件,
那么自定义事件,顾名思义,就是自己定义事件类型,自己定义事件处理函数,在合适的时候需要哪个事件类型,就去调用哪个处理程序

2.js所支持的浏览器默认事件

浏览器特定行为的事件,或者叫系统事件,js默认事件等等都行,大家知道我指的什么就行,下文我叫他js默认事件。
js默认事件的事件绑定,事件移出等一系列操作,相信大家都有用到过,如:

//DOM0级事件处理程序
var oDiv = document.getElementById('oDiv');
oDiv.onclick = function(){
    alert("你点击了我");
}

又或者

//DOM2级事件处理程序
var oDiv = document.getElementById('oDiv');

//非ie
oDiv.addEventListener("click",function(){
    alert("你点击了我");
},false);  

//ie
oDiv.attachEvent("onclick", function(){
    alert("你点击了我");
});

所有我就不做过多的研究,毕竟我们来讨论js自定义事件,这里给出一个我之前封装过的处理js默认事件的代码:

//跨浏览器的事件处理程序  
//调用时候直接用domEvent.addEvent( , , );直接调用  
//使用时候,先用addEvent添加事件,然后在handleFun里面直接写其他函数方法,如getEvent;  
//addEventListener和attachEvent---都是dom2级事件处理程序  
var domEvent = {  
    //element:dom对象,event:待处理的事件,handleFun:处理函数  
    //事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等  
    addEvent:function(element,event,handleFun){  
        //addEventListener----应用于mozilla  
        if(element.addEventListener){  
            element.addEventListener(event,handleFun,false);  
        }//attachEvent----应用于IE  
        else if(element.attachEvent){  
            element.attachEvent("on"+event,handleFun);  
        }//其他的选择dom0级事件处理程序  
        else{  
            //element.onclick===element["on"+event];  
            element["on"+event] = handleFun;  
        }  
    },  
    //事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等  
    removeEvent:function(element,event,handleFun){  
        //removeEventListener----应用于mozilla  
        if (element.removeEventListener) {  
            element.removeEventListener(event,handleFun,false);  
        }//detachEvent----应用于IE  
        else if (element.detachEvent) {  
            element.detachEvent("on"+event,handleFun);  
        }//其他的选择dom0级事件处理程序  
        else {  
            element["on"+event] = null;  
        }  
    },  
    //阻止事件冒泡  
    stopPropagation:function(event){  
        if(event.stopPropagation){  
            event.stopPropagation();  
        }else{  
            event.cancelBubble = true;//IE阻止事件冒泡,true代表阻止  
        }  
    },  
    //阻止事件默认行为  
    preventDefault:function(event){  
        if(event.preventDefault){  
            event.preventDefault();  
        }else{  
            event.returnValue = false;//IE阻止事件冒泡,false代表阻止  
        }  
    },  
    //获得事件元素  
    //event.target--非IE  
    //event.srcElement--IE  
    getElement:function(event){  
        return event.target || event.srcElement;  
    },  
    //获得事件  
    getEvent:function(event){  
        return event? event : window.event;  
    },  
    //获得事件类型  
    getType:function(event){  
        return event.type;  
    }  
};  

接下类我们进入正题,js自定义事件

3.对象直接量封装js自定义事件

根据上面的封装,我们可以这样构思

var eventTarget = {
    addEvent: function(){
        //添加事件
    },
    fireEvent: function(){
        //触发事件
    },
    removeEvent: function(){
        //移除事件
    }
};

相信这样大家还是比较好理解的,然后又有一个问题大家可以想到,那就是,js默认事件,js可以一一对应,知道那个是那个,那么我们的自定义事件呢,这个一一对应的映射表只能我们自己去建立,然后我这样

var eventTarget = {
    //保存映射
    handlers:{},
    addEvent: function(){
        //处理代码
    },
    fireEvent: function(){
        //触发代码
    },
    removeEvent: function(){
        //移出代码
    }
};

我是这样构建这个映射关系的

handlers = {
    "type1":[
        "fun1",
        "fun2",
       // "..."
    ],
    "type2":[
        "fun1",
        "fun2"
       // "..."
    ]
    //"..."
}

这样每一个类型可以有多个处理函数,以便于我们以后扩充
接下来就是代码方面的实战的,编写具体的处理代码了…

相信大家对于这个思路已经很清楚了,我直接附上代码

//直接量处理js自定义事件
var eventTarget = {
    //保存事件类型,处理函数数组映射
    handlers:{},
    //注册给定类型的事件处理程序,
    //type -> 自定义事件类型, handler -> 自定义事件回调函数
    addEvent: function(type, handler){
        //判断事件处理数组是否有该类型事件
        if(eventTarget.handlers[type] == undefined){
            eventTarget.handlers[type] = [];
        }
        //将处理事件push到事件处理数组里面
        eventTarget.handlers[type].push(handler);
    },
    //触发一个事件
    //event -> 为一个js对象,属性中至少包含type属性,
    //因为类型是必须的,其次可以传一些处理函数需要的其他变量参数。(这也是为什么要传js对象的原因)
    fireEvent: function(event){
        //判断是否存在该事件类型
        if(eventTarget.handlers[event.type] instanceof Array){
            var _handler = eventTarget.handlers[event.type];
            //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
            for(var i = 0; i < _handler.length; i++){
                //执行触发
                _handler[i](event);
            }
        }
    },
    //注销事件
    //type -> 自定义事件类型, handler -> 自定义事件回调函数
    removeEvent: function(type, handler){
        if(eventTarget.handlers[type] instanceof Array){
            var _handler = eventTarget.handlers[type];
            //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
            for(var i = 0; i < _handler.length; i++){
                //找出本次需要处理的事件下标
                if(_handler[i] == handler){
                    break;
                }
            }
            //删除处理事件
            _handler.splice(i, 1);
        }
    }
};

这是一种调用运行的方法

eventTarget.addEvent("eat",function(){
    console.log(123);   //123
});
eventTarget.fireEvent({type: "eat"});

这种方法有一个缺点,不能删除该处理事件,因为我们是用映射表做的,而且也不提倡,直接给映射表里面存这么多数据,有点多。

另一种方法,将处理事件提取出来(推荐)

function b(){
     console.log(123);
}
eventTarget.addEvent("eat",b);
eventTarget.fireEvent({
    type: "eat"
});                                     //123
eventTarget.removeEvent("eat",b);
eventTarget.fireEvent({type: "eat"});   //空

也可以这样,传递更多的参数

eventTarget.fireEvent({
    type: "eat",
    food: "banana"
}); 
function b(data){
     console.log(data.food);  //banana
}

总结:字面量这种方法,有点儿缺点,就是万一一不小心,把某个属性在handler函数里面,赋值null,这样会造成我们的的eventTarget 方法崩盘。看来原型应该是个好方法,更安全一点。

4.对象原型封装js自定义事件

由于前面思路基本都讲清楚了,这里我直接附上代码,大家可以研究下其中的利弊,或许你可以找到更好的方法解决Ta…

//自定义事件构造函数
function EventTarget(){
    //事件处理程序数组集合
    this.handlers = {};
}
//自定义事件的原型对象
EventTarget.prototype = {
    //设置原型构造函数链
    constructor: EventTarget,
    //注册给定类型的事件处理程序,
    //type -> 自定义事件类型, handler -> 自定义事件回调函数
    addEvent: function(type, handler){
        //判断事件处理数组是否有该类型事件
        if(typeof this.handlers[type] == 'undefined'){
            this.handlers[type] = [];
        }
        //将处理事件push到事件处理数组里面
        this.handlers[type].push(handler);
    },
    //触发一个事件
    //event -> 为一个js对象,属性中至少包含type属性,
    //因为类型是必须的,其次可以传一些处理函数需要的其他变量参数。(这也是为什么要传js对象的原因)
    fireEvent: function(event){
        //模拟真实事件的event
        if(!event.target){
            event.target = this;
        }
        //判断是否存在该事件类型
        if(this.handlers[event.type] instanceof Array){
            var handlers = this.handlers[event.type];
            //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
            for(var i = 0; i < handlers.length; i++){
                //执行触发
                handlers[i](event);
            }
        }
    },
    //注销事件
    //type -> 自定义事件类型, handler -> 自定义事件回调函数
    removeEvent: function(type, handler){
        //判断是否存在该事件类型
        if(this.handlers[type] instanceof Array){
            var handlers = this.handlers[type];
            //在同一个事件类型下的可能存在多种处理事件
            for(var i = 0; i < handlers.length; i++){
                //找出本次需要处理的事件下标
                if(handlers[i] == handler){
                    break;
                }
            }
            //从事件处理数组里面删除
            handlers.splice(i, 1);
        }
    }
};

调用方法

function b(){
    console.log(123);
}

var target = new EventTarget();
target.addEvent("eat", b);

target.fireEvent({
    type: "eat"
});                                 //123

原型这种方法,与直接量方法功能是一样的…

呼…也就基本说完了,如果哪里说得不对,欢迎讨论,后续有什么更好的理解也不断完善…

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值