2018年2月28日
背景
业务开发中遇到JS消息没被成功接收的问题。顺带复习一下 JS 事件。
为实现不同模块间的消息传递,我最初使用的是 jQuery trigger
和 on
。但不知何原因(猜测和模块加载、打包有关),模块间使用的并非是同一个 jQuery。也就是说消息队列挂在了不同的 $ 下,故模块间无法通信了。改为使用 CustomEvent 去传递消息。
CustomEvent
var event = new CustomEvent('eventName', { 'detail':data}); // 要传递的数据,放在detail 里面
Target.dispatchEvent(event);
Target.addEventListener('eventName', function(){...} );
事件流
事件是先经过捕获阶段,从上往下传;再经历冒泡阶段,从下往上传。
addEventListener 默认是在冒泡阶段调用函数,可添加第三参数 true,让事件在捕获阶段调用函数。
我一开始是使用 document 去发送和监听事件的。但联想到事件流,猜测是否子元素也可以监听这个document发出的事件?这里混淆了一点概念。
什么才是真正的子类?
若要触发的是 Target 容器,此时的 Target 就是“最子类了”,虽然它自己还有的子类 “child”,但事件流和这个child是无关的。所以这里的 child 容器接收不到消息是正常的。
接着在测试中,遇到了另一个无关紧要的问题。
问题
当 addEventListener 使用“冒泡阶段处理函数”时,父容器接收不到消息了。
而若是使用“捕获阶段处理函数”,则事件流正常。console结果如下:
在事件冒泡阶段调用处理函数
elem.addEventListener('build', function (e) { ... });
Target get the message: HELLO
在事件捕获阶段调用处理函数 elem.addEventListener('build', function (e) { ... }, true);
document get the message: HELLO
Parent get the message: HELLO
Target get the message: HELLO
原因
CustomEvent 默认是把冒泡功能禁用了。 需要手动传参去开启:
var event = new CustomEvent('eventName', { 'detail':'HELLO', 'bubbles': true});
JS 事件模型
JS 事件本质是观察者模式(Publish/Subscribe)。
肤浅地理解:
有一个全局变量;
每当 “on/listen/监听/注册/订阅” 一个事件时,就把对应的 key(事件名)和 value(callback)存进去。
每当 “trigger/dispatch/发布” 时,就找到相应的key(事件名),把对应的value(数组)里的每一个 callback,都执行一遍
但浏览器对此的实现更为复杂,它还得满足一系列的规则(JS事件流):
比如:$(A).trigger( "event_1" )
那么只有 A 以及 A 的父类容器,才有资格入选;
且这些入选者中,要提前有 监听(on)了这个 "event_1" 事件;
且浏览器会根据指令(捕获or冒泡),选择 callback 被调用的顺序。