• javascript自定义事件


    所谓自定义事件,就是有别于有别于带有浏览器特定行为的事件(类似clickmouseoversubmitkeydown等事件),事件名称可以随意定义,可以通过特定的方法进行添加,触发以及删除。
    可以定义一个数组,当添加事件的时候,我们push进去这个事件处理函数;当我们执行的时候,从头遍历这个数组中的每个事件处理函数,并执行。当多个事件以及对应数据处理函数添加后,我们最终会得到一个类似下面数据结构的对象:
    _listener = { 
        "click": [func1, func2], 
        "custom": [func3],
        "defined": [func4, func5, func6] 
    }
    

    实现方式

    1、全局变量定义

    var _listener = {}; 
    var addEvent = function(type, fn) { 
        // 添加 
    }; 
    var fireEvent = function(type) { 
        // 触发 
    }; 
    var removeEvent = function(type, fn) {
         // 删除
    };
    

    2、减少全局变量的方法之一就是使用全局对象(其他如闭包),全局变量属性方法等都是暴露而且都是唯一的,一旦某个关键属性(如_listeners)不小心在某事件处reset了下,则整个全局的自定义事件都会崩溃。

    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;
        }
    };
    
    Event.addEvent("alert", function() {
        alert("弹出!");
    });
    
    // 触发自定义alert事件
    Event.fireEvent("alert");

    3、原型模式实现

    var EventTarget = function() {
        this._listener = {};
    };
    
    EventTarget.prototype = {
        constructor: this,
        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 myEvents = new EventTarget();
    var yourEvents = new EventTarget();

    这样,即使myEvents的事件容器_listener跛掉,也不会污染yourEvents中的自定义事件。

    var myEvents = new EventTarget();
    myEvents.addEvents({
        "once": function() {
            alert("该弹框只会出现一次!");    
            this.removeEvent("once");
        },
        "infinity": function() {
            alert("每次点击页面,该弹框都会出现!");    
        }
    });
    
    document.onclick = function(e) {
        e = e || window.event;
        var target = e.target || e.srcElement;
        if (!target || !/input|pre/i.test(target.tagName)) {
            myEvents.fireEvents(["once", "infinity"]);
        }
    };

    3、DOM自定义事件

    平常所使用的事件基本都是与DOM元素相关的,例如点击按钮,文本输入等,这些为自带浏览器行为事件,而自定义事件与这些行为无关。

    element.addEventListener("alert", function() { 
        alert("弹出!"); 
    });

    这里的alert就属于自定义事件,后面的function就是自定义事件函数。而这个自定义事件是直接绑定在名为element的DOM元素上的,因此,这个称之为自定义DOM事件。

    HTMLElement.prototype.addEvent = function(type, fn, capture) { 
        var el = this; 
        if (window.addEventListener) { 
            el.addEventListener(type, function(e) { 
                fn.call(el, e); 
            }, capture); 
        }else if (window.attachEvent) { 
            el.attachEvent("on" + type, function(e) { 
                fn.call(el, e); 
            }); 
        } 
    };

    由于IE6, IE7浏览器的DOM水平较低,无法直接进行扩展,因此,原型扩展的方法在这两个浏览器下是行不通的。要想让这两个浏览器也支持addEvent方法,只能是页面载入时候遍历所有DOM,然后每个都直接添加addEvent方法了。

    var elAll = document.all, lenAll = elAll.length;
    for (var iAll=0; iAll<lenAll; iAll+=1) {
        elAll[iAll].addEvent = function(type, fn) {
            var el = this;
            el.attachEvent("on" + type, function(e) {
                fn.call(el, e);
            });
        };
    }
    document.getElementById("image").addEvent("click", function() {
        alert("这是:" + this.alt);    
    });

    基于DOM扩展缺点有:缺少标准无规律、提高冲突可能性、性能以及浏览器支持。

    扩展名字任意命,很有可能就会与未来DOM浏览器本身支持的方法相互冲突;扩展无规律,很有可能出现A和B同名不同功能的扩展而造成冲突;IE6-7浏览器下所有扩展都要通过遍历支持,其性能开销可想而知;另外IE8对DOM扩展的支持并不完整,例如其支持Element.prototype,却没有HTMLElement.prototype
    4、伪DOM自定义事件
    var $ = function(el) { 
        return new _$(el); 
    }; 
    var _$ = function(el) { 
        this.el = el; 
    }; 
    _$.prototype = { 
        constructor: this,
        addEvent: function(){},
        fireEvent: function() {},
        removeEvent: function() {}
    }

    可以使用类似$(dom).addEvent()的语法为元素添加事件了(包括不包含浏览器行为的自定义事件)

    addEvent: function(type, fn, capture) { 
        var el = this.el; 
        if (window.addEventListener) { 
            el.addEventListener(type, fn, capture); 
        } else if (window.attachEvent) { 
            el.attachEvent("on" + type, fn); 
        } 
        return this; 
    }

    自定义事件添加容易,但自定义事件与浏览器行为无关,同时浏览器没有直接的触发事件的方法。

    1. 对于标准浏览器

    $(dom).addEvent("alert", function() { 
        alert("do something"); 
    });
    // 创建 
    var evt = document.createEvent("HTMLEvents"); 
    // 初始化 
    evt.initEvent("alert", false, false); 
    // 触发, 即弹出文字 
    dom.dispatchEvent(evt);

    createEvent()方法返回新创建的Event对象,支持一个参数,表示事件类型

    initEvent()方法用于初始化通过DocumentEvent接口创建的Event的值。支持三个参数:initEvent(eventName, canBubble, preventDefault). 分别表示事件名称,是否可以冒泡,是否阻止事件的默认操作。
    dispatchEvent()就是触发执行了,dom.dispatchEvent(eventObject), 参数eventObject表示事件对象,是createEvent()方法返回的创建的Event对象。
    addEventListener('dataavailable', handler, false);dataavailable 新事件名、handler 触发的函数、false 是否是扑获事件模型

    2. 对于IE浏览器

      由于向下很多版本的浏览器都不支持document.createEvent()方法,因此我们需要另辟蹊径(据说IE有document.createEventObject()event.fireEvent()方法,但是不支持自定义事件)
      IE浏览器有不少自给自足的东西,例如下面要说的这个"propertychange"事件,属性改变即触发事件。例如文本框value值改变,或是元素id改变,或是绑定的事件改变等等。
    当我们添加自定义事件的时候,顺便给元素添加一个自定义属性即可
    dom.evtAlert = "2015-09-017";
    dom.attachEvent("onpropertychange", function(e) { 
        if (e.propertyName == "evtAlert") { 
            fn.call(this); 
        }
    });
    //触发自定义事件的时候,只要修改DOM上自定义的evtAlert属性
    dom.evtAlert = Math.random(); // 值变成随机数
    //自定义事件的删除
    dom.detachEvent("onpropertychange", evt);
    var $ = function(el) {
        return new _$(el);    
    };
    var _$ = function(el) {
        this.el = (el && el.nodeType == 1)? el: document;
    };
    _$.prototype = {
        constructor: this,
        addEvent: function(type, fn, capture) {
            var el = this.el;
            if (window.addEventListener) {
                el.addEventListener(type, fn, capture);
                var ev = document.createEvent("HTMLEvents");
                ev.initEvent(type, capture || false, false);
                
                if (!el["ev" + type]) {
                    el["ev" + type] = ev;
                }  
            } else if (window.attachEvent) {
                el.attachEvent("on" + type, fn);    
                if (isNaN(el["cu" + type])) {
                    // 自定义属性
                    el["cu" + type] = 0; 
                }   
                var fnEv = function(event) {
                    if (event.propertyName == "cu" + type) { fn.call(el); }
                };
                el.attachEvent("onpropertychange", fnEv);     
                if (!el["ev" + type]) {
                    el["ev" + type] = [fnEv];
                } else {
                    el["ev" + type].push(fnEv);    
                }
            }
            return this;
        },
        fireEvent: function(type) {
            var el = this.el;
            if (typeof type === "string") {
                if (document.dispatchEvent) {
                    if (el["ev" + type]) {
                        el.dispatchEvent(el["ev" + type]);
                    }
                } else if (document.attachEvent) {
                    el["cu" + type]++;
                }    
            }    
            return this;
        },
        removeEvent: function(type, fn, capture) {
            var el = this.el;
            if (window.removeEventListener) {
                el.removeEventListener(type, fn, capture || false);
            } else if (document.attachEvent) {
                el.detachEvent("on" + type, fn);
                var arrEv = el["ev" + type];
                if (arrEv instanceof Array) {
                    for (var i=0; i<arrEv.length; i+=1) {
                        el.detachEvent("onpropertychange", arrEv[i]);
                    }
                }
            }
            return this;    
        }
    };
    
    $(elImage)
        .addEvent("click", funClick);
        .addEvent("alert", funAlert1)
        .addEvent("alert", funAlert2);
    $(elImage).fireEvent("alert");

    KISSY中自定义事件

     

     
     

      

  • 相关阅读:
    详细解说仿制QQ列表 展开和收起列表
    带大家一步一步封装聊天键盘(三)新增功能不要错过哟
    带大家一步一步封装一个聊天键盘(二)
    带大家一步一步的封装一个聊天键盘(一)
    iOS中的屏幕适配之Autolayout(初级)
    iOS开发中tableViewCell的悬浮效果
    [Leetcode] 1343. Maximum Product of Splitted Binary Tree | 分裂二叉树的最大乘积
    [Leetcode] 560. Subarray Sum Equals K | 和为K的子数组
    爆炸!iOS资源大礼包(持续更新...)
    Objective-C探究alloc方法的实现
  • 原文地址:https://www.cnblogs.com/chenlogin/p/4816351.html
Copyright © 2020-2023  润新知