新浪微博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方法,需要相应做修改。

posted @ 2012-12-24 16:17  Jsilk  阅读(313)  评论(0)    收藏  举报