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

时间: 2024-11-06 22:17:05

基于CSS class的事件监听管理机制 (转)的相关文章

事件监听:诀别Android繁琐的事件注册机制——view.setOnXXXXListener 滚犊子

好久没写过随笔了......windows phone生态没起来,属于.net阵营的我最近工作不是太忙,闲暇之余就心血来潮开始研究安卓.先简单扯两句这几天学习下来对java事件监听机制的一点感触.客观地讲,java的事件监听机制相比.net好原始,暂不说委托.lamda.泛型等的繁琐,仅一个事件监听,就需要各种listener才能实现,比如安卓里到处都是view.setOnXXXXListener.被C#“语法糖”和宇宙第一IDE惯坏的我真心有点不习惯,于是就决定写个工具来封装这些烦人的list

js事件监听机制(事件捕获)总结

在前端开发过程中我们经常会遇到给页面元素添加事件的问题,添加事件的js方法也很多,有直接加到页面结构上的,有使用一些js事件监听的方法,由于各个浏览器对事件冒泡事件监听的机制不同,le浏览器只有事件冒泡,没有事件监听的机制,对于事件监听的兼容性问题是最大的难题: 1.直接把事件的方法写在页面结构上 function eventfun(){ //console.log(this); } <input type="button" onclick="eventfun()&qu

js基础——事件绑定(事件监听)

JavaScript事件一共有三种监听方法分别如下: 1.事件监听一夹杂在html标签内 1 <div id="box" onClick="alert('HELLO WORLD')"> 2 <div id="box2" onClick="notice();"></div> 3 <div id="box3" onClick="service('HELLO W

RN性能优化及事件监听

自从React Native出世,虽然官方一直尽可能的优化其性能,为了能让其媲美原生App的速度,但是现实感觉有点不尽人意.接下来介绍下实践中遇到的一些性能问题以及优化方案. 一.StackNavigator页面切换动画优化 场景:在navigation还没出来时,导航路由使用NavigatorIOS来实现,页面切换是很流畅的,但是用了StackNavigator navigation发现页面切换会使JS线程出现严重的掉帧(卡顿现象): 原因:NavigatorIOS的切换动画是跑在UI主线程上

关于v4包的Fragment过渡动画的事件监听无响应问题解决

项目中部分功能模块采用了单Activity+多Fragment模式,当Fragment切换时,需要在过渡动画执行完后做一些操作,通常就是在自己封装的FragmentBase中重写onCreateAnimation方法,创建一个Animation对象,并添加动画的事件监听.而最近升级了v4包后,突然发现添加的动画事件监听无响应了.通过查看源码,发现在v4包中关于Fragment管理类FragmentManagerImpl中,在获取Animation对象后,也添加了对动画的监听事件,也就覆盖了我自己

spring boot 源码赏析之事件监听

使用spring Boot已经快1年多了,期间一直想点开springboot源码查看,但由于种种原因一直未能如愿(主要是人类的惰性...),今天就拿springboot 的监听事件祭刀. springboot 中常用的事件监听主要有ApplicationStartedEvent,ApplicationEnviromentPreparedEvent,ApplicationPreparedEvent,ApplicationStoppedEvent等.用于监听springboot生命周期中的各种事件.

JS 事件绑定、事件监听、事件委托详细介绍

事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 在DOM中直接绑定事件 我们可以在DOM元素上绑定onclick.onmouseover.onmouseout.onmousedown.onmouseup.ondblclick.onkeyd

让 select 的 option 标签支持事件监听(如复制操作)

这标题,让option支持事件监听,应该不难的呀,有什么好讲的? 其实还是有的,默认在浏览器代码是无法直接对option标签进行操作的,不仅包括JS事件监听,还是CSS样式设置 查了一些资料,姑且认为它是系统OS级别处理的 想自定义option的样式,很多人会建议用 <ul> <li> 标签来辅助同步操作与值 想对option进行事件监听,有一个tip:当给select显示设置了size 属性且值 大于1 时,才能监听 近来产品也提了个鼠标操作复制option值的需求,就利用这个s

ExtJs内的datefield控件选择日期过后的事件监听select

[摘要]: 选择时间过后我们为什么需要监听事件?一般有这样一种情况,那就是用于比较两个时间大小或者需要判断在哪个时间点上需要做什么样的操作.基于这样的种种情况,我们很有必要琢磨一下datefield控件的日期选择事件了的. 那么我们如何添加日期选择事件呢?针对这样一个问题,网上有很多ExtJs的盆友想到了change事件,就是当文本框内日期值就上一次值来说不同时,触发该事件,change事件添加核心代码如下所示: { xtype: 'datefield', name: 'birthday', f