zoukankan      html  css  js  c++  java
  • 转: JS自定义事件的定义和触发(createEvent, dispatchEvent)

    四、伪DOM自定义事件

    这里的“伪DOM自定义事件”是自己定义的一个名词,用来区分DOM自定义事件的。例如jQuery库,其是基于包装器(一个包含DOM元素的中间层)扩展事件的,既与DOM相关,又不直接是DOM,因此,称之为“伪DOM自定义事件”。

    //zxx: 下面即将展示的代码目的在于学习与认识,要想实际应用可能还需要在细节上做些调整。例如,下面测试的包装器仅仅只是包裹DOM元素,并非选择器之类;$符号未增加冲突处理,且几个重要方法都暴露在全局环境中,没有闭包保护等。

    原型以及new函数构造不是本文重点,因此,下面这个仅展示:

    var $ = function(el) {
        return new _$(el);    
    };
    var _$ = function(el) {
        this.el = el;
    };
    _$.prototype = {
        constructor: this,
        addEvent: function() {
            // ...
        },
        fireEvent: function() {
            // ...
        },
        removeEvent: function() {
            // ...
        }
    }

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

    自定义事件的添加
    如果只考虑事件添加,我们的工作其实很简单,根据支持情况,addEventListenerattachEvent方法分别添加事件(attachEvent方法后添加事件先触发)即可:

    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;
    }
    

    显然,事情不会这么简单,有句古话叫做“上山容易下山难”,自定义事件添加容易,但是如何触发它们呢?——考虑到自定义事件与浏览器行为无关,同时浏览器没有直接的触发事件的方法。

    自定义事件的触发
    又是不可避免的,由于浏览器兼容性问题,我们要分开说了,针对标准浏览器和IE6/7等考古浏览器。

    1. 对于标准浏览器,其提供了可供元素触发的方法:element.dispatchEvent(). 不过,在使用该方法之前,我们还需要做其他两件事,及创建和初始化。因此,总结说来就是:

    document.createEvent()
    event.initEvent()
    element.dispatchEvent()

    举个板栗:

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

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

    参数事件接口初始化方法
    HTMLEvents HTMLEvent initEvent()
    MouseEvents MouseEvent initMouseEvent()
    UIEvents UIEvent initUIEvent()

    关于createEvent()方法我自己了解也不是很深入,不想滥竽充数,误人子弟,所以您有疑问我可能作答不了,希望对熟知该方法的人可以做进一步的解释说明(例如事件接口与document关系,UIEvent是什么东西等)。

    initEvent()方法用于初始化通过DocumentEvent接口创建的Event的值。支持三个参数:initEvent(eventName, canBubble, preventDefault). 分别表示事件名称,是否可以冒泡,是否阻止事件的默认操作。

    dispatchEvent()就是触发执行了,dom.dispatchEvent(eventObject), 参数eventObject表示事件对象,是createEvent()方法返回的创建的Event对象。

    2. 对于IE浏览器,由于向下很多版本的浏览器都不支持document.createEvent()方法,因此我们需要另辟蹊径(据说IE有document.createEventObject()event.fireEvent()方法,但是不支持自定义事件~~)。

    IE浏览器有不少自给自足的东西,例如下面要说的这个"propertychange"事件,顾名思意,就是属性改变即触发的事件。例如文本框value值改变,或是元素id改变,或是绑定的事件改变等等。

    我们可以利用这个IE私有的东西实现自定义事件的触发,大家可以先花几分钟想想……

    // zxx: 假设几分钟已经过去了……

    大家现在有思路了没?其实说穿了很简单,当我们添加自定义事件的时候,顺便给元素添加一个自定义属性即可。例如,我们添加自定义名为"alert"的自定义事件,顺便我们可以对元素做点小手脚:

    dom.evtAlert = "2012-04-01";

    再顺便把自定义事件fn塞到"propertychange"事件中:

    dom.attachEvent("onpropertychange", function(e) {
        if (e.propertyName == "evtAlert") {
            fn.call(this);
        }
    });

    这个,当我们需要触发自定义事件的时候,只要修改DOM上自定义的evtAlert属性的值即可:

    dom.evtAlert = Math.random();	// 值变成随机数

    此时就会触发dom上绑定的onpropertychange事件,又因为修改的属性名正好是"evtAlert", 于是自定义的fn就会被执行。这就是IE浏览器下事件触发实现的完整机制,应该说讲得还是蛮细的。

    自定义事件的删除
    与触发事件不同,事件删除,各个浏览器都提供了对于的时间删除方法,如removeEventListenerdetachEvent。不过呢,对于IE浏览器,还要多删除一个事件,就是为了实现触发功能额外增加的onpropertychange事件:

    dom.detachEvent("onpropertychange", evt);

    var $ = function(el) {
        return new _$(el);    
    };
    var _$ = function(el) {
        this.el = (el && el.nodeType == 1)? el: document;
    };
    _$.prototype = {
        constructor: _$,
        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);
                
                // 在元素上存储绑定的propertychange事件,方便删除
                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) {
                        // 删除该方法名下所有绑定的propertychange事件
                        el.detachEvent("onpropertychange", arrEv[i]);
                    }
                }
            }
            return this;    
        }
    };
  • 相关阅读:
    redis发布订阅
    redis学习笔记(面试题)
    redis安全 (error) NOAUTH Authentication required
    HDU3001 Travelling —— 状压DP(三进制)
    POJ3616 Milking Time —— DP
    POJ3186 Treats for the Cows —— DP
    HDU1074 Doing Homework —— 状压DP
    POJ1661 Help Jimmy —— DP
    HDU1260 Tickets —— DP
    HDU1176 免费馅饼 —— DP
  • 原文地址:https://www.cnblogs.com/stephenykk/p/4861420.html
Copyright © 2011-2022 走看看