• 基于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

  • 相关阅读:
    (五)Spring Cloud教程——Config(F版本)
    (四)Spring Cloud教程——Zuul(F版本)
    (三)Spring Cloud教程——Hystrix(F版本)
    (二)Spring Cloud教程——Ribbon 和 Feign(F版本)
    (一)Spring Cloud教程——Eureka(F版本)
    3年的坚持,最终造就著作——《Learninghard C#学习笔记》
    WPF快速入门系列(9)——WPF任务管理工具实现
    WPF快速入门系列(8)——MVVM快速入门
    [WPF实用技巧]如何使WPF的TreeView节点之间有连线
    WPF快速入门系列(7)——深入解析WPF模板
  • 原文地址:https://www.cnblogs.com/lvke/p/4288188.html
Copyright © 2020-2023  润新知