美文网首页Web前端之路
JavaScript 自定义事件(一)

JavaScript 自定义事件(一)

作者: 卓三阳 | 来源:发表于2017-11-04 01:55 被阅读79次

    所谓自定义事件,就是有别于带有浏览器特定行为的事件(鼠标事件,键盘事件,html事件等),事件名称可以自定义,可以通过特定的方法进行添加,触发以及删除。自定义事件相当于是 观察者模式 ,可以把复杂逻辑解耦,代码可以写的很清晰,而且很容易复用。

    一.JS事件应用初印象

    我觉得更为准确的说,下面列举的是Dom事件的应用。我们在这里可以通过了解JS关于Dom事件的绑定,触发,解绑来一步一步书写我们自己的JS自定义事件。关于自定义事件,我决定分两步讲解。第一步,也就是这篇文章将要提到的循序渐进的一步一步完善自定义JS事件。第二步,我们封装一个与Dom元素有关的自定义事件函数。

    // 事件绑定
    function addEvent(element, eType, handler, bol) {
        if(element.addEventListener){           //如果支持addEventListener
            element.addEventListener(eType, handler bol);
        }else if(element.attachEvent){          //如果支持attachEvent
            element.attachEvent("on"+eType, handler);
        }else{                                  //否则使用兼容的onclick绑定
            element["on"+eType] = handle;
        }
    }
    // 事件解绑
    function removeEvent(element, eType, handler, bol) {
        if(element.addEventListener){
            element.removeEventListener(eType, handler, bol);
        }else if(element.attachEvent){
            element.detachEvent("on"+eType, handler);
        }else{
            element["on"+eType] = null;
        }
    }
    //实例应用
    var patty=document.getElementById("patty");
    var sayHello=function (){
       alert("Hello!!!");
    }
    addEvent(patty,click,sayHello,false); //添加点击事件
    

    这里我们关注下click事件的实现。我们可以很清楚的将其分成三部分:添加,触发,删除。
    同时,我们必须考虑一个问题:事件和其处理函数应该怎样存储。事件分为很多种,click, mouseover, submit, keydown等等,每一种事件下又可以添加处理函数。这种一对多的映射关系,我们可以很自然想到用下面这样数据结构来存储事件。

    _listener = {
    "click": [func1, func2],
    "custom": [func3],
    ...
    }
    

    二.自定义我们自己的JS事件

    1.函数式实现
    var _listener = {};   //存储事件和其处理函数
    var addEvent = function(type, fn) {
          //添加
    };
    var fireEvent = function(type) {
         //触发
    };
    var removeEvent = function(type, fn) {
         //删除
    };
    
    //添加eat事件
    addEvent("eat",function(){
      alert("eat an apple!");
    })
    // 触发自定义eat事件
    fireEvent(eat);
    

    我们没有详细展示函数式实现的代码,是因为这种写法较为基础且过多地暴露全局变量,我们稍微了解即可,具体函数地实现,我们在下面的方式会实现。

    2.用字面量方式实现
    var Event = {
        _listeners: {},    
        // 添加
        addEvent: function(type, fn) {
            if (typeof this._listeners[type] === "undefined") {
                this._listeners[type] = [];
            }
            if (typeof fn === "function") {
                this._listeners[type].push(fn);
            }    
            return this;
        },
        // 触发
        fireEvent: function(type) {
            var arrayEvent = this._listeners[type];
            if (arrayEvent instanceof Array) {
                for (var i=0, length=arrayEvent.length; i<length; i+=1) {
                    if (typeof arrayEvent[i] === "function") {
                        arrayEvent[i]({ type: type });    
                    }
                }
            }    
            return this;
        },
        // 删除
        removeEvent: function(type, fn) {
            var arrayEvent = this._listeners[type];
            if (typeof type === "string" && arrayEvent instanceof Array) {
                if (typeof fn === "function") {
                    // 清除当前type类型事件下对应fn方法
                    for (var i=0, length=arrayEvent.length; i<length; i+=1){
                        if (arrayEvent[i] === fn){
                            this._listeners[type].splice(i, 1);
                            break;
                        }
                    }
                } else {
                    // 如果仅仅参数type, 或参数fn邪魔外道,则所有type类型事件清除
                    delete this._listeners[type];
                }
            }
            return this;
        }
    };
    
    //添加eat事件
    Event.addEvent("eat",function(){
      alert("eat an apple!");
    })
    // 触发自定义eat事件
    Event.fireEvent(eat);
    

    字面量实现虽然减少了全局变量,但是其属性方法等都是暴露而且都是唯一的,一旦某个关键属性(如_listeners)不小心在某事件处reset了,则整个全局的自定义事件都会崩溃。因此,我们可以进一步改进,例如,使用原型链继承,让继承的属性(如_listeners)即使出问题也不会影响全局。

    3.原型模式实现
    var EventTarget = function() {
        this._listener = {};
    };
    
    EventTarget.prototype = {
        constructor:EventTarget,
        addEvent: function(type, fn) {
            if (typeof type === "string" && typeof fn === "function") {
                if (typeof this._listener[type] === "undefined") {
                    this._listener[type] = [fn];
                } else {
                    this._listener[type].push(fn);    
                }
            }
            return this;
        },
        addEvents: function(obj) {
            obj = typeof obj === "object"? obj : {};
            var type;
            for (type in obj) {
                if ( type && typeof obj[type] === "function") {
                    this.addEvent(type, obj[type]);    
                }
            }
            return this;
        },
        fireEvent: function(type) {
            if (type && this._listener[type]) {
                var events = {
                    type: type,
                    target: this    
                };
                
                for (var length = this._listener[type].length, start=0; start<length; start+=1) {
                    this._listener[type][start].call(this, events);
                }
            }
            return this;
        },
        fireEvents: function(array) {
            if (array instanceof Array) {
                for (var i=0, length = array.length; i<length; i+=1) {
                    this.fireEvent(array[i]);
                }
            }
            return this;
        },
        removeEvent: function(type, key) {
            var listeners = this._listener[type];
            if (listeners instanceof Array) {
                if (typeof key === "function") {
                    for (var i=0, length=listeners.length; i<length; i+=1){
                        if (listeners[i] === key){
                            listeners.splice(i, 1);
                            break;
                        }
                    }
                } else if (key instanceof Array) {
                    for (var lis=0, lenkey = key.length; lis<lenkey; lis+=1) {
                        this.removeEvent(type, key[lenkey]);
                    }
                } else {
                    delete this._listener[type];
                }
            }
            return this;
        },
        removeEvents: function(params) {
            if (params instanceof Array) {
                for (var i=0, length = params.length; i<length; i+=1) {
                    this.removeEvent(params[i]);
                }    
            } else if (typeof params === "object") {
                for (var type in params) {
                    this.removeEvent(type, params[type]);    
                }
            }
            return this;    
        }
    };
    
    //使用
    var event1=new EventTarget();
    var event2=new EventTarget();
    

    这样我们发现,event1和event2在共享方法的同时,又有自己的_listener 属性,彼此之间不会污染。

    我相信大家通过上面的了解,对JS自定义事件一定有更深的理解了,OK这次就先讲这么多,谢谢观看!

    相关文章

      网友评论

        本文标题:JavaScript 自定义事件(一)

        本文链接:https://www.haomeiwen.com/subject/zvpxmxtx.html