基於CSS class的事件監聽管理機制 (轉)


背景:

做了那么多web項目,總會發現到處都是事件綁定,同一個按鈕的執行動作,也許會分布在多個js文件中。

而且對於js動態生成的文檔片段,里面會經常出現“onclick=...”之類的代碼,一到功能升級,或者代碼重構的時候,

就會發現,這個難度以及工作量,和重寫一遍沒什么區別,有時候甚至工作量更大!

 

基於各種情況的分析、以及以往的經驗總結,百度空間則有了一套自己的事件監聽管理機制:基於CSS class的事件監聽管理機制

 

方案:

1、js代碼中,不出現對某節點的事件監聽,如:$('#elm').click(function(e){})

2、html代碼中,不出現inline的事件監聽,如:<input type="button" value="OK" onclick="doSth();">

3、所有需要進行事件監聽的節點,都配置一個(或一類)class

4、每個獨立的子模塊(或頁面)有一個單獨的listener_manager.js文件

5、在listener_manager.js中,對所有的事件監聽進行集中管理,類似“任務管理器”

6、只對一個大容器(HTMLElement)進行事件綁定,獲取觸發源,判斷源是否包含了某個class,包含則觸發其對應的監聽

7、對於js動態生成的文檔片段,依然通過此方法,指定class即可;這個能完美的實現類似jQuery中的live

 

關於實現:

先來看一個非常重要的方法:事件的綁定與觸發

/**
  * 通過css class的方式來注冊事件
  * @param {HTMLElement} elmContainer 需要進行全局監聽的HTML節點
  * @param {Array} arrEvent 需要監聽的事件列表
  * @param {Object} classMap css-class和event-function之間的映射
  * @param {Function} fnCustom 每次事件觸發后需要執行的自定義操作
  */
window.addEventMap = function (elmContainer,arrEvent,classMap,fnCustom){
     $.each(arrEvent, function (i,item){
         // 只對一個節點進行各種事件監聽
         $(elmContainer).bind(item, function (evt){
             // 獲取時間觸發源
             var evtTarget = evt.target || evt.srcElement;
             // 對觸發源DOM進行安全性判斷
             if (!evtTarget) return false ;
       
             for ( var className in classMap[item]){
                 // 獲取事件驅動方法
                 var fnListener = classMap[item][className];
                 // 當前節點滿足觸發條件,則觸發事件
                 if ((evtTarget.className && $(evtTarget).hasClass(className)) ) {
                     fnListener.call(evtTarget,evt);
                     break ;
                 }
                 // 如果其父節點滿足,也可以觸發該事件
                 else if (ancestor = $(evtTarget).parents( '.' + className)[0]){
                     fnListener.call(ancestor,evt);
                     break ;
                 }
             }
                   
             //支持自定義操作
             if ( typeof fnCustom === 'function' ){
                 fnCustom.call(evt);
             }
         });
     });
};

拿一個簡單的應用來舉例說明,先看listener_manager.js的內容:

/**
  * 注冊命名空間
  */
window.registNS( 'qhome' );
      
/**
  * 事件監聽程序
  * @return {[type]}
  */
qhome.ListenerMgr = ( function (){
      
     /**
      * 展開所有轉發理由
      * @param  {[type]} e [description]
      * @return {[type]}
      */
     var _fn_a_expand_reson = function (e){
     };
      
     /**
      * 隱藏轉發理由
      * @param  {[type]} e [description]
      * @return {[type]}
      */
     var _fn_a_collapse_reason = function (e){
     };
      
     /**
      * 音樂播放
      * @return {[type]}
      */
     var _fn_play_music = function (e){
     };
          
     /**
      * 評論
      */
     var _fn_a_reply = function (evt){
     };
          
     /**
      * 轉載
      */
     var _fn_a_repost = function (evt){
     };
      
     /**
      * 鼠標在頭像上划過時,顯示:上傳頭像
      * @param  {[type]} e [description]
      * @return {[type]}
      */
     var _fn_wrapper_avatar_over = function (e){
     };
      
     /**
      * 鼠標在頭像上划出時,隱藏:上傳頭像
      * @param  {[type]} e [description]
      * @return {[type]}
      */
     var _fn_wrapper_avatar_out = function (e){
     };
      
     /**
      * 在這里通過DOM節點的className來對應該節點需要增加的事件監聽
      */
     var _className2ListenerMap = {
         click : {
             'a-expand-reason'   : _fn_a_expand_reson,
             'a-collapse-reason' : _fn_a_collapse_reason,
             'q-play-music'      : _fn_play_music,
             'q-progressbar'     : _fn_play_music,
             'a-reply'           : _fn_a_reply,
             'a-repost'          : _fn_a_repost
         },
         mouseover : {
             'wraper-avatar'     : _fn_wrapper_avatar_over
         },
         mouseout : {
             'wraper-avatar'     : _fn_wrapper_avatar_out
         }
     };
          
     /**
      * 啟動事件監聽管理器
      * @return {[type]}
      */
     var _run = function (){
         window.addEventMap(
             $( '.mod-page-main' ),                //需要進行事件監聽的容器
             [ 'click' , 'mouseover' , 'mouseout' ],   //event列表
             _className2ListenerMap              //class映射表
         );
     };
          
     return {
         run : _run
     };
})();

關於window.registNS,在這里有講到。

從上面的事件監聽管理器中可以很容易的看出,每一個(或一類)CSS class,唯一對應一個監聽程序。

如上代碼中第66到81行,就是定義CSS class和event function之間的映射關系。

如上代碼中第70行和71行,class為q-play-music,以及class為q-progressbar的兩個節點,當發生click事件的時候,其具體動作都可以交由_fn_play_music處理。而且由js動態生成的節點中,也會包含class為這兩個的節點,其事件監聽就會自動的被ListenerMgr捕獲並處理,這個地方也就是jQuery中的live方式。

如上代碼中第88行就是調用了一個核心方法,用戶事件綁定。

 

在web應用需要初始化的時候,即可調用事件監聽管理器的run方法,啟動事件監聽管理器:

 

// 啟動事件監聽管理器
qhome.ListenerMgr.run();

這個時候,監聽器即開始工作,只要頁面上有上面動靜,符合規則的節點都會被捕獲到。

 

收益:

這種事件監聽管理的機制,能將web應用中所有的事件監聽進行統一管理,其初始化的入口,有且僅有一個,其作為一個單獨的plugin而存在。代碼集中,功能獨立,便於管理,維護成本低。

 

這是一種集中式的事件管理機制。

轉自Alien的筆記:http://www.baidufe.com/item/98947f853cc68032af53.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM