zoukankan      html  css  js  c++  java
  • 据说每个大牛、小牛都应该有自己的库——Event处理

    今天抽时间写了一部分Event处理方面的函数愈发的觉得jQuery的优秀,自己前期的想法太粗糙,造成后面这些函数参数很多,操作很很不直观,看样子是要重构的节奏,还好小伙儿伴们安慰,架构都是改出来的。继续探索吧

    浏览器兼容性

    写Event处理的库函数一个难点就在于浏览器兼容性问题,在IE低版本浏览器中事件对象始终在window.event属性中,而在其它浏览器中event会作为事件处理程序中作为第一个参数传入。而且其Event对象的属性和方法也有诸多差异,在JavaScript与HTML交互——事件中基本有所总结,不过还是抄一段关于事件处理程序绑定方面的差异

    1. 参数个数不相同,这个最直观,addEventListener有三个参数,attachEvent只有两个,attachEvent添加的事件处理程序只能发生在冒泡阶段,addEventListener第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)

    2. 第一个参数意义不同,addEventListener第一个参数是事件类型(比如click,load),而attachEvent第一个参数指明的是事件处理函数名称(onclick,onload)

    3. 事件处理程序的作用域不相同,addEventListener得作用域是元素本身,this是指的触发元素,而attachEvent事件处理程序会在全局变量内运行,this是window

    4. 为一个事件添加多个事件处理程序时,执行顺序不同,addEventListener添加会按照添加顺序执行,而attachEvent添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理,不要指望浏览器

    最简单的四个

    先写四个最简单的

    • getEvent:获取事件对象
    • getTarget:获取事件源对象
    • preventDefault:阻止事件默认行为
    • stopPropagation:阻止事件冒泡
    (function (window) {
                var ssLib = {
                    getEvent: function (e) {
                        return e ? e : window.event;
                    },
    
                    getTarget: function (e) {
                        var e = this.getEvent(e);
                        return e.target || e.srcElement;
                    },
    
                    preventDefault: function (e) {
                        var e = this.getEvent(e);
                        if (e.preventDefault) {
                            e.preventDefault();
                        } else {
                            e.returnValue = false;
                        }
                    },
    
                    stopPropagation: function (e) {
                        var e = this.getEvent(e);
                        if (e.stopPropagation) {
                            e.stopPropagation();
                        } else {
                            e.cancelBubble = true;
                        }
                    }
                };
                window.ssLib = window.ss = ssLib;
            })(window);

    代码很简单,相信聪明的小伙儿伴们一看就懂,就不多做解释了

    addEvent/removeEvent

    • addEvent:为元素绑定事件处理程序
    • removeEvent:移除元素事件处理程序
    addEvent: function (element, type, handler, key) {
                        var key = key || handler;
                        if (element[type + key])
                            return false;
                        if (typeof element.addEventListener != "undefined") {
                            element[type + key] = handler;
                            element.addEventListener(type, handler, false);
                        }
                        else {
                            element['e' + type + key] = handler;
                            element[type + key] = function () {
                                element['e' + type + key](window.event);
                            };
                            element.attachEvent('on' + type, element[type + key]);
                        }
                        return true;
                    },
    
                    removeEvent: function (element, type, key) {
                        if (!element[type + key])
                            return false;
    
                        if (typeof element.removeEventListener != "undefined") {
                            element.removeEventListener(type, element[type + key], false);
                        }
                        else {
                            element.detachEvent("on" + type, element[type + key]);
                            element['e' + type + key] = null;
                        }
    
                        element[type + key] = null;
                        return true;
                    },

    这两个函数兼容性写法有很多,结合了很多大牛的写法后我用的上面版本,这么些看似很复杂,实际上主要解决了上面提到的、除了多个事件处理程序执行顺序问题的浏览器兼容性问题,比较难看懂的IE绑定部分就是为了处理this而写的。

    在使用的时候,可以显示传入一个key用于内部识别绑定函数,如果不绑定则使用handler本身作为key,所以可以这么用

    ssLib.addEvent(element,'click',function(){},'test');
            ssLib.removeEvent(element,'click','test');
            
            function handler(){}
            
            ssLib.addEvent(element,'click',handler);
            ssLib.removeEvent(element,'click',handler);

    on/off

    这个是看到jQuery的on/delegate和YUI 的delegate后萌发的想法,不过平时老用人家的没自己写过,仓促写了一个,感慨颇多,还是jQuery好使

    on: function (parent, type, handler,validater,key) {
                        var _key=key || handler;
                        parent['on'+type+_key]=function (e) {
                            var target = ssLib.getTarget(e);
                            var isFire = validater(target);
                            if (isFire) {
                                target[type + _key] = handler;
                                target[type + _key](e);
                            }
                        };
                        
                        ssLib.addEvent(parent, type,parent['on'+type+_key] , key);
                    },
    
                    off: function (parent, type, key) {
                        if(typeof key=='function'){
                            ssLib.removeEvent(parent, type, parent['on'+type+key]);
                        }else{
                            ssLib.removeEvent(parent, type, key);
                        }
                        parent['on'+type+key]=null;
                    }

    写法和刚才类似,不停的绕来绕去也是解决this问题,可以这么用

    <div id="test">
            <div id="t1">T1</div>
            <div id="t2">T2</div>
        </div>
    var parent = document.getElementById('test');
            
            var handler=function () {
                alert(this.id);
            }
            
            var validater=function (obj) {
                if(obj.parentNode.id=='test'){
                    return true;
                }else{
                    return false;
                }
            }
            
            ss.on(parent, 'click', handler, validater);
            
            ss.off(parent,'click',handler);
            
            ss.on(parent, 'click', handler, validater,'delegate');
            
            ss.off(parent,'click','delegate');

    ready

    用过jQuery的同学肯定知道这个函数,写自己库的时候是各种惊羡啊,于是乎加到了自己的库里

    ready: function (fn) {
                        if (document.addEventListener) {
                            document.addEventListener("DOMContentLoaded", function () {
                                document.removeEventListener("DOMContentLoaded", arguments.callee, false);// 防止多次调用
                                fn();
                            }, false);
                        } else if (document.addEvent) {
                            var doc = window.document, done = false;
                            // only fire once
                            var init = function () {
                                if (!done) {
                                    done = true;
                                    fn();
                                }
                            };
                            // polling for no errors
                            (function () {
                                try {
                                    // throws errors until after ondocumentready
                                    doc.documentElement.doScroll('left');// 文档加载完成后此方法可用
                                } catch (e) {
                                    setTimeout(arguments.callee, 50);
                                    return;
                                }
                                // no errors, fire
                                init();
                            })();
                            // trying to always fire before onload
                            doc.onreadystatechange = function () {
                                if (doc.readyState == 'complete') {
                                    doc.onreadystatechange = null;
                                    init();
                                }
                            };
                        }
                    }

    要想看懂上面代码最好看看An alternative for DOMContentLoaded on Internet Explorer

    在现代浏览器中文档加载完后会触发“DOMContentLoaded”事件,而在底版本IE中文档加载完成后会doScroll方法会生效

    Event部分源代码

    (function (window) {
                var ssLib = {
                    getEvent: function (e) {
                        return e ? e : window.event;
                    },
    
                    getTarget: function (e) {
                        var e = this.getEvent(e);
                        return e.target || e.srcElement;
                    },
    
                    preventDefault: function (e) {
                        var e = this.getEvent(e);
                        if (e.preventDefault) {
                            e.preventDefault();
                        } else {
                            e.returnValue = false;
                        }
                    },
    
                    stopPropagation: function (e) {
                        var e = this.getEvent(e);
                        if (e.stopPropagation) {
                            e.stopPropagation();
                        } else {
                            e.cancelBubble = true;
                        }
                    },
    
                    addEvent: function (element, type, handler, key) {
                        var key = key || handler;
                        if (element[type + key])
                            return false;
                        if (typeof element.addEventListener != "undefined") {
                            element[type + key] = handler;
                            element.addEventListener(type, handler, false);
                        }
                        else {
                            element['e' + type + key] = handler;
                            element[type + key] = function () {
                                element['e' + type + key](window.event);
                            };
                            element.attachEvent('on' + type, element[type + key]);
                        }
                        return true;
                    },
    
                    removeEvent: function (element, type, key) {
                        if (!element[type + key])
                            return false;
    
                        if (typeof element.removeEventListener != "undefined") {
                            element.removeEventListener(type, element[type + key], false);
                        }
                        else {
                            element.detachEvent("on" + type, element[type + key]);
                            element['e' + type + key] = null;
                        }
    
                        element[type + key] = null;
                        return true;
                    },
    
                    ready: function (fn) {
                        if (document.addEventListener) {
                            document.addEventListener("DOMContentLoaded", function () {
                                document.removeEventListener("DOMContentLoaded", arguments.callee, false);
                                fn();
                            }, false);
                        } else if (document.attachEvent) {
                            var doc = window.document, done = false;
                            // only fire once
                            var init = function () {
                                if (!done) {
                                    done = true;
                                    fn();
                                }
                            };
                            // polling for no errors
                            (function () {
                                try {
                                    // throws errors until after ondocumentready
                                    doc.documentElement.doScroll('left');
                                } catch (e) {
                                    setTimeout(arguments.callee, 50);
                                    return;
                                }
                                // no errors, fire
                                init();
                            })();
                            // trying to always fire before onload
                            doc.onreadystatechange = function () {
                                if (doc.readyState == 'complete') {
                                    doc.onreadystatechange = null;
                                    init();
                                }
                            };
                        }
                    },
    
                    on: function (parent, type, handler, validater, key) {
                        var _key = key || handler;
                        parent['on' + type + _key] = function (e) {
                            var target = ssLib.getTarget(e);
                            var isFire = validater(target);
                            if (isFire) {
                                target[type + _key] = handler;
                                target[type + _key](e);
                            }
                        };
    
                        ssLib.addEvent(parent, type, parent['on' + type + _key], key);
                    },
    
                    off: function (parent, type, key) {
                        if (typeof key == 'function') {
                            ssLib.removeEvent(parent, type, parent['on' + type + key]);
                        } else {
                            ssLib.removeEvent(parent, type, key);
                        }
                        parent['on' + type + key] = null;
                    }
                };
                window.ssLib = window.ss = ssLib;
            })(window);
  • 相关阅读:
    树形dp--P2014 [CTSC1997]选课
    背包变形--P1759 通天之潜水
    区间dp--P1880 [NOI1995]石子合并
    动态规划--P2758 编辑距离
    筛法--CF449C Jzzhu and Apples
    BZOJ3998: [TJOI2015]弦论(后缀自动机,Parent树)
    BZOJ3530: [Sdoi2014]数数(Trie图,数位Dp)
    BZOJ1444: [Jsoi2009]有趣的游戏(Trie图,矩乘)
    BZOJ1195: [HNOI2006]最短母串(Trie图,搜索)
    BZOJ3238: [Ahoi2013]差异(后缀数组)
  • 原文地址:https://www.cnblogs.com/dolphinX/p/3302360.html
Copyright © 2011-2022 走看看