JavaScript的事件广播与侦听

先来看html页面的主要结构:

<div id="wrap" class="wrap">
	<input type="text" name="test" id="test" size="35" />
</div>

<div class="wrap" style="position:absolute;left:20px;top:250px;">
	left:<div id="left" class="inner"></div>
</div>
<div class="wrap" style="position:absolute;left:550px;top:250px;">
	right:<div id="right" class="inner"></div>
</div>
需求:id为test的输入框(以下简称A)值改变时,实时改变id为left和right的内容。

如果这是一个小项目,一个简单的页面,那这个实现这个需求是非常简单的。但如果是一个比较复杂的项目,处理left和right都各有很多业务逻辑,需要把这两部分放在不同模块里,那么这时候就不得不考虑一种新的解决方案。而且随着时间的增加,可能在某天产品又会增加一个新模块,也要求A的值变化时作出相应反应。这时候,问题就变得越来越复杂了。

事件广播和侦听,是实现这种需求最好的方案。

那什么是事件广播和侦听,我个人的理解是:当发生某一事件时,在某个频道上发出广播,向注册到该频道上的所有监听者发送信息数据,让各监听者在自己内部做相应处理。这样做可以解耦各模块,很方便地拆卸每个模块。

下面我们用事件广播和侦听来实现上面的需求,步骤如下:

1、创建一个频道

//频道说明,创建一个频道时写清楚
//说明这个频道是干什么的;什么时候会广播这个频道;广播时传的数据结构或参数形式,等等
Utils.Listener.createChannel('index.test.change'); //可以使用这种类似命名空间的字符串做频道名,这里表示index页下的test发生改变
2、在不同代码模块里向频道index.test.change添加监听者。这里监听者的名字是changeLeft,第三个参数是收到广播消息时应该做什么处理,即回调函数。

    Utils.Listener.add('index.test.change', 'changeLeft', function(data){
        
        $E('left').innerHTML = data; //改变left的内容
        
        //Utils.Listener.remove('index.test.change', 'changeLeft'); //移除频道监听
        
        //Utils.Listener.remove('index.test.change'); //删除整个频道
        
    });
添加另外一个监听者changeRight,当然在一个频道上你可以添加很多很多的监听者:

    Utils.Listener.add('index.test.change', 'changeRight', function(data){
        
        $E('right').innerHTML = data;
        
    });
3、给A框绑定keyup事件,让它的值发生变化时发出广播
    Core.Events.addEvent($E('test'), function(){
        //在频道 index.test.change 发出广播
        Utils.Listener.broadcast('index.test.change', $E('test').value); //第二个参数为广播时发出的信息,你可以添加多个参数
        
        //在频道 index.test.change 向监听者 changeLeft 发出单播
        //Utils.Listener.unicast('index.test.change', 'changeLeft', $E('test').value+'. Wahaha~');
        
    }, 'keyup');
大功告成!改变A框的值时,left和right已能跟随改变内容。

当然,事件广播和侦听的功能远远不只这个,你可以用它来实现各相互独立的业务模块之间的通信,等等。还有,做web编辑器的时候,也需要用到,因为你可能要在iframe的body上多次监听keyup之类的事件,如果每次都绑事件,那效率太低了,所以如果使用事件广播的话,只需要在body上绑一个keyup事件,然后广播这个事件就行了。

最后把广播和侦听的代码贴出来:

/**
 * 事件侦听、广播、单播
 * @method
 * @example
        //创建频道“unameChange”
        Utils.Listener.createChannel('unameChange');
        //创建频道“uname.change”---天然支持“伪”命名空间!!!
        Utils.Listener.createChannel('uname.change');

        //添加unameChange的监听者“updateTray”
        Utils.Listener.add('unameChange', 'updateTray', function(){
            //TODO
        });
        //添加unameChange的监听者“changeFootbar”
        Utils.Listener.add('unameChange', 'changeFootbar', function(){
            //TODO
        });
        //发出广播
        Utils.Listener.broadcast('unameChange', someData);
        //发出广播
        Utils.Listener.broadcast('uname.change', someData);
        //发出单播
        Utils.Listener.unicast('uname.change', 'changeFootbar', someData);
 */
var Utils = {};
!function(){
    if(Utils.Listener){
        return;
    }
    var _channels = {},
        slice = Array.prototype.slice;

    Utils.Listener = {
        //channelName 频道名,天然支持“伪”命名空间。例如:uname.change
        createChannel: function(channelName){
            if( _channels[channelName] ){
                traceError('Channel "'+channelName+'" has been defined!');
            }else{
                _channels[channelName] = {};
            }
        },
        //channelName 监听频道
        //listenerName 监听者
        //handler 发生广播时的执行函数
        add: function(channelName, listenerName, handler){
            var channel = _channels[channelName];
            if( !channel ){
                traceError('Channel "'+channelName+'" has NOT been defined!');
                return;
            }
            if( channel[listenerName] ){
                traceError(channelName+':'+listenerName+'" has been defined!');
                return;
            }
            channel[listenerName] = handler;
        },
        broadcast: function(channelName/*, data...*/){
            var channel = _channels[channelName];
            if( channel ){
                for(var p in channel){
                    if( channel[p] ){
                        channel[p].apply(null, slice.call(arguments,1));
                    }
                }
            }
        },
        unicast: function(channelName, listenerName/*, data...*/){
            var channel = _channels[channelName];
            if( channel && channel[listenerName]){
                channel[listenerName].apply(null, slice.call(arguments,2));
            }
        },
        //channelName 频道名
        //listenerName 可选,如果没有,将删除整个频道
        remove: function(channelName, listenerName){
            var channel = _channels[channelName];
            if( channel ){
                if(listenerName){
                    channel[listenerName] = null;
                    delete channel[listenerName];
                }else{
                    channel = null;
                    delete _channels[channelName];
                }
            }
        }
        
    };

}();

欢迎大家提出改进建议。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值