其实就是一个封装事件绑定的库,但是看到了一些技巧记录下来 github地址:https://github.com/bevacqua/crossvent/blob/master/src/crossvent.js
从出口开始 1 2 3 4 5 module .exports = { add : addEvent, remove : removeEvent, fabricate : fabricateEvent };
add和remove函数 addEvent
实际上就是判断window是否有addEventListener
,如果没有就用attachEvent
,remove同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 var addEvent = addEventEasy;var removeEvent = removeEventEasy;if (!global .addEventListener) { addEvent = addEventHard; removeEvent = removeEventHard; } function addEventEasy (el, type, fn, capturing ) { return el.addEventListener(type, fn, capturing); } function addEventHard (el, type, fn ) { return el.attachEvent('on' + type, wrap(el, type, fn)); } function removeEventEasy (el, type, fn, capturing ) { return el.removeEventListener(type, fn, capturing); } function removeEventHard (el, type, fn ) { var listener = unwrap(el, type, fn); if (listener) { return el.detachEvent('on' + type, listener); } }
这里需要注意:在不支持addEventListener
的时候,使用attachEvent
和detachEvent
事件回调函数多做了一层wrap
和unwrap
处理
wrap和unwrap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var hardCache = [];function wrap (el, type, fn ) { var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn); hardCache.push({ wrapper : wrapper, element : el, type : type, fn : fn }); return wrapper; } function unwrap (el, type, fn ) { var i = find(el, type, fn); if (i) { var wrapper = hardCache[i].wrapper; hardCache.splice(i, 1 ); return wrapper; } } function find (el, type, fn ) { var i, item; for (i = 0 ; i < hardCache.length; i++) { item = hardCache[i]; if (item.element === el && item.type === type && item.fn === fn) { return i; } } }
可以看到是利用一个数组存储事件回调,在绑定事件和解绑事件同时对hardCache数组进行操作,但是为啥要这样呢。可以看到在wrap函数的第一行var wrapper = unwrap(el, type, fn) || wrapperFactory(el, type, fn);
,实际上就是如果之前没有绑定这个type的事件,就调用wrapperFactory
对fn
进行二次封装,wrapperFactory
这个封装主要是为了解决event参数内的属性的兼容性 。
这就解释了刚才提出的疑问,就是因为多了这层解决浏览器兼容性的包装,在解绑事件的时候,如果只是传递原来的fn,则不能解绑事件,需要一个hardCache数组来存取事件回调。
wrapperFactory 1 2 3 4 5 6 7 8 9 10 function wrapperFactory (el, type, fn ) { return function wrapper (originalEvent ) { var e = originalEvent || global .event; e.target = e.target || e.srcElement; e.preventDefault = e.preventDefault || function preventDefault ( ) { e.returnValue = false ; }; e.stopPropagation = e.stopPropagation || function stopPropagation ( ) { e.cancelBubble = true ; }; e.which = e.which || e.keyCode; fn.call(el, e); }; }
这层封装可以使event的各个属性在不同浏览器下行为一致。
add和revome就讲完了,我们来看看导出的最后一个函数fabricate
fabricate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function fabricateEvent (el, type, model ) { var e = eventmap.indexOf(type) === -1 ? makeCustomEvent() : makeClassicEvent(); if (el.dispatchEvent) { el.dispatchEvent(e); } else { el.fireEvent('on' + type, e); } function makeClassicEvent ( ) { var e; if (doc.createEvent) { e = doc.createEvent('Event' ); e.initEvent(type, true , true ); } else if (doc.createEventObject) { e = doc.createEventObject(); } return e; } function makeCustomEvent ( ) { return new customEvent(type, { detail : model }); } }
eventmap
是原生事件名的集合,如果没有在eventmap
找到则创建一个自定义事件,否则创建一个原生事件,然后触发事件。
那么eventmap是怎么生成的呢,我们下面来看看
获取原生事件名集合 1 2 3 4 5 6 7 8 9 10 11 var eventmap = [];var eventname = '' ;var ron = /^on/ ;for (eventname in global ) { if (ron.test(eventname)) { eventmap.push(eventname.slice(2 )); } } module .exports = eventmap;
其实就是正则匹配window上的开头为on的属性名
总结 看到了挺多浏览器兼容的处理方式,以及原生事件名获取的骚操作,不过仍有一些兼容方式没看懂是兼容哪些浏览器的。