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,所以刚才例子才会返回undefined,而不是元素id
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
最后
今天太晚了要去睡觉了,明天会优化一下代码并解释一下ready函数