新浪微博focus和blur的事件代理的实现
废话不多说,咱就不解释什么叫事件,什么叫事件代理这类问题了。
通常来讲,事件代理都是通过事件冒泡来实现的,理由也很简单,IE8及其之前的IE版本,只支持事件冒泡,不捕获(目前IE9和IE10已经支持DOM标准事件)。对于绝大多数的事件来讲,通过冒泡来实现代理是没有什么问题的,但是,对于focus和blur事件,问题就出来了。
focus和blur事件是不支持事件冒泡的,因此,我们无法通过冒泡进行事件代理,这就使得我们必须为每一个input框、textarea框等都add一个focus和blur事件,有时候,这是让人很不爽的,比如说,微公益里个人求助中,每一步都有一个大表单需要填写,有很多的input和textarea,需求里肯定有这么一步:输入的合法性判断,有问题的,给个错误提示;后来又增加了一个需求:每个表单框给个友情提示……问题就这样出现了,咱们必须for一堆的表单框,每个都添加focus和blur事件,很是让人不爽的。
在微博RIA工作的都使用过STK.delegatedEvent()进行事件代理,这货也是通过事件冒泡实现的事件代理,咱就没法用这货进行focus和blur事件的代理了,怎么实现呢?
由于DOM标准当年没有提出一个解决方案,IE和Opera为了解决这问题,各自提出了一种方案,IE中使用focusin和focusout,Opera则使用DOMFocusIn和DOMFocusOut实现,它们都可以被冒泡。由于IE的比较像样,被W3C拿去当DOM3级事件标准了。
懒得写太多解释了,因为原理很简单,IE里把focus事件改为focusin,把blur事件改写成focusout,其他浏览器都使用捕获方式。直接看一下微博里修改后的delegatedEvent事件对象吧。
1 /** 2 * 事件代理对象,主要是解决focus和blur的代理问题 3 * 兼容core包下的delegatedEvent方法 4 * 5 * @id STK.core.evt.delegatedEvent 6 * @author Robin Young | yonglin@staff.sina.com.cn 7 * @version 1.0 8 * 9 * @modified @ZengWq572 | weiqun1@staff.sina.com.cn 10 */ 11 STK.register('kit.evt.delegatedEvent', function($){ 12 13 var checkContains = function(list,el){ 14 for(var i = 0, len = list.length; i < len; i += 1){ 15 if($.core.dom.contains(list[i],el)){ 16 return true; 17 } 18 } 19 return false; 20 }; 21 22 // 不使用STK包的addEvent方法,将DOM标准下的事件添加改为使用捕获方式 23 var addEvent = function(node, type, handler) { 24 node = $.E(node); 25 if (node == null) { 26 return false; 27 } 28 if (typeof handler !== "function") { 29 return false; 30 } 31 if (node.addEventListener) { 32 node.addEventListener(type, handler, true); 33 } else if (node.attachEvent) { 34 node.attachEvent('on' + type, handler); 35 } else { 36 node['on' + type] = handler; 37 } 38 return true; 39 }; 40 41 var isIE = $.core.util.browser.IE; 42 43 return function(actEl,expEls){ 44 if(!$.core.dom.isNode(actEl)){ 45 throw 'core.evt.delegatedEvent need an Element as first Parameter'; 46 } 47 if(!expEls){ 48 expEls = []; 49 } 50 if($.core.arr.isArray(expEls)){ 51 expEls = [expEls]; 52 } 53 var evtList = {}; 54 var bindEvent = function(e){ 55 var evt = $.core.evt.fixEvent(e); 56 var el = evt.target; 57 var type = e.type; 58 doDelegated(el, type, evt); 59 }; 60 61 var doDelegated = function(el, type, evt){ 62 var actionType = null; 63 var changeTarget = function(){ 64 var path, lis, tg; 65 path = el.getAttribute('action-target'); 66 if(path){ 67 lis = $.core.dom.sizzle(path, actEl); 68 if(lis.length){ 69 tg = evt.target = lis[0]; 70 } 71 }; 72 changeTarget = $.core.func.empty; 73 return tg; 74 }; 75 var checkBuble = function(){ 76 var tg = changeTarget() || el; 77 if(evtList[type] && evtList[type][actionType]){ 78 return evtList[type][actionType]({ 79 'evt' : evt, 80 'el' : tg, 81 'box' : actEl, 82 'data' : $.core.json.queryToJson(tg.getAttribute('action-data') || '') 83 }); 84 }else{ 85 return true; 86 } 87 }; 88 if(checkContains(expEls,el)){ 89 return false; 90 }else if(!$.core.dom.contains(actEl, el)){ 91 return false; 92 }else{ 93 while(el && el !== actEl){ 94 if(el.nodeType === 1){ 95 actionType = el.getAttribute('action-type'); 96 if(actionType && checkBuble() === false){ 97 break; 98 } 99 } 100 el = el.parentNode; 101 } 102 103 } 104 }; 105 106 var that = {}; 107 /** 108 * 添加代理事件 109 * @method add 110 * @param {String} funcName 111 * @param {String} evtType 112 * @param {Function} process 113 * @return {void} 114 */ 115 that.add = function(funcName, evtType, process){ 116 if(isIE && (evtType == 'focus' || evtType == 'blur')) { 117 evtType = evtType == 'focus' ? 'focusin' : 'focusout'; 118 } 119 if(!evtList[evtType]){ 120 evtList[evtType] = {}; 121 addEvent(actEl, evtType, bindEvent); 122 } 123 var ns = evtList[evtType]; 124 ns[funcName] = process; 125 }; 126 /** 127 * 移出代理事件 128 * @method remove 129 * @param {String} funcName 130 * @param {String} evtType 131 * @return {void} 132 */ 133 that.remove = function(funcName, evtType){ 134 if(isIE && (evtType == 'focus' || evtType == 'blur')) { 135 evtType = evtType == 'focus' ? 'focusin' : 'focusout'; 136 } 137 if(evtList[evtType]){ 138 delete evtList[evtType][funcName]; 139 if($.core.obj.isEmpty(evtList[evtType])){ 140 delete evtList[evtType]; 141 $.core.evt.removeEvent(actEl, evtType, bindEvent); 142 } 143 } 144 }; 145 146 that.destroy = function(){ 147 for(var k in evtList){ 148 for(var l in evtList[k]){ 149 delete evtList[k][l]; 150 } 151 delete evtList[k]; 152 $.core.evt.removeEvent(actEl, k, bindEvent); 153 } 154 }; 155 return that; 156 }; 157 });
仅修改add和remove两个方法,其他fireAction等方法没有做修改,若需要使用这些fireXX方法,需要相应做修改。

浙公网安备 33010602011771号