据说每个大牛、小牛都应该有自己的库——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);


    本文转自魏琼东博客园博客,原文链接: http://www.cnblogs.com/dolphinX/p/3302360.html ,如需转载请自行联系原作者

相关文章
|
测试技术
你真的知道什么是冒烟测试吗?
大家好,我是阿萨。日常工作中,经常都会提到冒烟测试。那么什么是冒烟测试呢?
3613 0
你真的知道什么是冒烟测试吗?
|
12月前
|
自然语言处理 供应链 数据可视化
跨境电商团队如何管理远程项目?选择看板工具时需要考虑的关键因素
随着跨境电商的迅速发展,全球化带来了巨大商机,但也带来了跨时区、语言和文化差异等协作挑战。为了提高远程协作效率,本文推荐了五款适合跨境电商团队使用的看板工具:板栗看板、Trello、Asana、ClickUp 和 Monday.com。这些工具通过可视化的任务管理和进度追踪,帮助团队高效协作、确保信息一致性和任务顺利完成。
跨境电商团队如何管理远程项目?选择看板工具时需要考虑的关键因素
|
12月前
|
安全 API C语言
Python程序的安全逆向(关于我的OPENAI的APIkey是如何被盗的)
本文介绍了如何使用C语言编写一个简单的文件加解密程序,并讨论了如何为编译后的软件添加图标。此外,文章还探讨了Python的.pyc、.pyd等文件的原理,以及如何生成和使用.pyd文件来增强代码的安全性。通过视频和教程,作者详细讲解了生成.pyd文件的过程,并分享了逆向分析.pyd文件的方法。最后,文章提到可以通过定制Python解释器来进一步保护源代码。
295 6
|
监控 关系型数据库 MySQL
zabbix agent集成percona监控MySQL的插件实战案例
这篇文章是关于如何使用Percona监控插件集成Zabbix agent来监控MySQL的实战案例。
246 2
zabbix agent集成percona监控MySQL的插件实战案例
详细教程:扫码提交表单后,数据直接推送到企业微信、钉钉、飞书群聊
在草料制作的表单中,填表人扫码填写并提交数据后,这些信息可以立即通过企业微信、钉钉或飞书自动推送到相应的群聊中,实现即时共享和沟通,提升团队协作效率。
551 2
|
12月前
|
运维 网络安全
解决ssh: connect to host IP port 22: Connection timed out报错(scp传文件指定端口)
通过这些步骤和方法,您可以有效解决“ssh: connect to host IP port 22: Connection timed out”问题,并顺利使用 `scp`命令传输文件。
11409 7
|
Ubuntu
window下解压.tar文件的软件
公司开发环境使用的是Ubuntu/debian,经常打包成tar文件,导致在window使用winrar和7-zip都无法很好的解压,甚至解压后出现一个无法使用的文件,现在有一个新的免费软件可以替代了: Bandizip多国语言,自带简体中文,免费、高效、跨平台,体验良好 附:异次元评测:http://www.
1878 0
|
前端开发 JavaScript Oracle
程序员如何成为自由职业者或者数字游民?
程序员如何成为自由职业者或者数字游民?
671 0
|
运维 网络协议 Cloud Native
阿里云SDWAN解决方案部署指南
利用阿里云丰富云网络产品,提供完整的SDWAN企业分支互联/企业上云解决方案。本次SDWAN解决方案部署指导场景以解决企业大陆分支机构、亚太IDC及云上VPC构建企业办公组网为例。意在指导客户在项目部署实施阶段可参考本文自助完成业务上线的搭建。本文内容已经对外披露。
2121 1
阿里云SDWAN解决方案部署指南
|
XML Linux PHP
Powerlink协议使用极简入门教程
Powerlink协议使用极简入门教程