探索 layui 的 onevent 和 event

讨论 已结 精帖
29 5441
岁月小偷
悬赏:50飞吻
使用layui的同学,不知道大家是不是跟我一样其实基本不会用到layui提供的onevent和event这两个方法。但是其实我们每天都跟他们打交道,因为组件的事件监听基本都是基于onevent做的,只不过是在它的基础上又给加了一个“糖衣”,但是平时的话还是基本不会用onevent监听什么事件的。

今天社区有个问题 http://fly.layui.com/jie/27137/ 就是定义了onevent然后执行调用了一下layui.event结果出现执行了两次监听的情况。追了一下发现可能还是源码的一个处理逻辑有点问题。改一下自己定义onevent和执行event中带了(filter)然后就正常了。
看似问题解决了,但是也引起了我的好奇,对原先的设计有了一个大胆的猜想,是不是事件也有母子关系,不带filter的为母带filter的为子?动起手验证一下,确实达到了我猜想的效果,就是不知道是不是设计者的设计思路就不知道了,因为确实官方没有对onevent和event做什么介绍,只说了阅读源码,只能自行理解了。
例子效果如下

代码如下,如果想在本地试一下的同学请到这 https://pan.baidu.com/s/1uD7M84CwMTBzeLZBkOFd8A 下载,不要拷贝社区的代码,不敢保证社区的代码发出去会不会被控件转义了
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>layui</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="js/layui/src/css/layui.css" media="all">
<!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 -->
</head>
<body>
<div style="margin: 10px;">
<div class="layui-inline">
<label class="layui-form-label">小明:</label>
<div class="layui-input-inline">
<input name="phone" autocomplete="off" class="layui-input" style="width: 240px;">
</div>
</div>
<button class="layui-btn" lay-even="sendMessageA">发送</button>
</div>
<div style="margin: 10px;">
<div class="layui-inline">
<label class="layui-form-label">小红:</label>
<div class="layui-input-inline">
<input name="phone" autocomplete="off" class="layui-input" style="width: 240px;">
</div>
</div>
<button class="layui-btn" lay-even="sendMessageB">发送</button>
</div>
<div style="margin: 10px;">
<div class="layui-inline">
<label class="layui-form-label">系统时间</label>
<button class="layui-btn" lay-data="{align: 'center'}" lay-even="sendMessageS">发送</button>
</div>
</div>


<div id="messageView"
style="width: 480px; height: 300px;border: 1px solid #1E9FFF;margin-left: 120px;padding: 10px;overflow: auto;"></div>

<script src="js/layui/src/layui.js" charset="utf-8"></script>
<!-- 注意:如果你直接复制所有代码到本地,上述js路径需要改成你本地的 -->
<script>
layui.use(['jquery', 'util'], function ($, util) {
var active = {
sendMessageA: function () {
layui.event("message", "send(A)", {
align: 'left',
user: '小明',
msg: $(this).prev().find('input').val()
});
$(this).prev().find('input').val('');
},
sendMessageB: function () {
layui.event("message", "send(B)", {
align: 'right',
user: '小红',
msg: $(this).prev().find('input').val()
});
$(this).prev().find('input').val('');
},
sendMessageS: function () {
layui.event("message", "send", {
align: 'center'
});
}
};
var divElem = $('#messageView');
// 母事件,所有message.send(*)都会触发这个
layui.onevent("message", "send", function (params) {
divElem.append('<div style="text-align: ' + (params.align || 'center') + ';">' + util.toDateString(null, 'yyyy-MM-dd HH:mm:ss') + '</div>');
var timer = setTimeout(function () {
if (timer) {
clearTimeout(timer);
}
divElem.scrollTop(divElem[0].scrollHeight)
}, 50)
});
// 子事件A
layui.onevent("message", "send(A)", function (params) {
divElem.append('<div style="margin: 6px 0;text-align: left;">' + (params.msg || '<span style="color: #1E9FFF">没有输入,只是想你了一下</span>') + '</div>')
});
// 子事件B
layui.onevent("message", "send(B)", function (params) {
divElem.append('<div style="margin: 6px 0;text-align: right;color: pink;">' + (params.msg || '<span style="color: #1E9FFF">没有输入,只是想你了一下</span>') + '</div>')
});

$('.layui-btn').click(function () {
var elem = $(this),
event = elem.attr('lay-even');
typeof active[event] === 'function' && active[event].apply(this);
});
});

</script>

</body>
</html>

页面功能和代码相应的解析
首先定义了3个message的send事件一个“母事件”send(姑且这么叫,瞎编的可能不合适[哈哈] ),负责在聊天面板中追加系统时间,两个子事件send(A), send(B),分别就是小明和小红的聊天内容的输出啦。可以看到子事件中没有去操作调用母事件(输出发消息的时间);
然后就是三个人了,一个小明,一个小红,还有一个系统按钮,触发的方法就是去执行layui.event来触发我们定义的对应的layui.onevent事件,同样可以看到,小明或者小红发送消息的时候也没有去调用系统按钮的功能。
最终的效果就是,小明或者小红输入信息发送,会首先触发母事件然后再触发子事件,也就是先输出一个发送消息的时间然后再是发送的内容。而系统时间按钮就只触发了自身的事件。
到此感觉这可能是一个以后可以好好利用的机制。[色] 但是!还不能高兴太早!
首先如果小伙伴有兴趣的可以把我的测试代码下下来换个layui的路径跑一下试一下。应该就会很快发现一个问题,点击系统时间发送的时候会有两个连着输出了,不要怀疑是不是双击了,不是,这个就是最开始说的那个问题,不在赘叙,我说的源码的不太合理的地方是event的最后片段

可见如果是一个母事件,filter为空那么会执行两次回调。改成下面的

就正常了,其他的子事件也没有问题。

到此觉得很顺利,但是同时也觉得有一个隐患,如果定义事件的时候子事件早于母事件定义会是什么情况呢?比如把onevent send(A)的代码段放到send的前面,那么问题就来了,小明发送消息的时候会是消息在前时间在后面。
[哈哈] [哈哈] [哈哈] 来几个表情压压惊,其实也是可以接受理解的,假如,真把他们当成母子事件,那是不是应该先有母事件才能有子事件呢?OK,这回顺利,总不能先有了孩子再有妈妈吧。当然所谓的母子事件纯粹周末空闲倒腾出来的,是不是设计者设计的时候就考虑进去了这个只能官方的才能解密了[嘻嘻]
回帖
  • 胡歌
    2018-5-20
    [good]
    0 回复
  • 一阔树
    2018-5-20
    用阻止冒泡可以去掉一次出发,但是接下来return无效了。
    5 回复
  • @时光的旅行 不好意思,刚看了你发的贴了额,iframe这个我不擅长,以为一直以来都是做单页面的,只有在学校用过iframe,所以基本没啥经验,刚想这两天有人发了分享,不过看到你也在里面评论了应该是看过了,我觉得可能关键问题在于如何找到对应的iframe,然后就可以使用该iframe的document了,里面的属性之类的估计就都能得到了,不知道是不是这个意思,现在有事在忙,等闲下来就自己做测试例子看看能不能搞定,可以的话就再@ 你一下额。
    5 回复
  • @时光的旅行 [good] 不错不错
    3 回复
  • @胡歌 周末愉快[嘻嘻]
    1 回复
  • @岁月小偷 大哥可不可以帮我看看啊
    1 回复
  • 谢谢大哥了
    1 回复
  • 一阔树
    2018-5-20
    [good] [good] [good] 楼主精神佳佳佳!
    0 回复
  • @一阔树 [嘻嘻] 摸索学习
    0 回复
  • @岁月小偷 大哥能帮我一下么
    0 回复
  • 非常感谢你的回复,谢谢
    0 回复
  • @时光的旅行 嗯嗯,差不多忙完了,我去写个测试的例子看看能不能搞出来。一会好了就到你的帖子下面回复额。
    0 回复
  • @岁月小偷 谢谢哥们,我弄好了,很感谢你
    0 回复
  • maplemei
    2018-5-23
    [good]
    设计之初估计就是为layui本身而服务的, 不曾想被你扒拉出来了[哈哈] , 然后写自己测试了一下, 改了一下写法, 避免了改动源码, 思路就是符合 xxx(xxx), 模式
    //1.构建自己的监听方式
    LayuiTest.prototype.on = function(events, callback) {
    return layui.onevent.call(this, MOD_NAME, `LayuiTest(${events})`, callback);
    };

    //2.使用event
    layui.event.call(this, MOD_NAME, `LayuiTest(${id})`, params);

    //3.使用监听
    layui.LayuiTest.on('xxxx', function(){
    console.log(1)
    });
    其实本质就是加了一层华丽的外衣
    0 回复
  • Immanuel
    2018-5-23
    支持支持[太开心]
    0 回复
  • @MapleMei 曲线救国[嘻嘻] 。因为其实官方没过多介绍,然后又是息息相关的两函数,因为任何组件都是离不开事件的,每个组件的事件又是基于这个这两函数封装的,所以理解多一些还是有用的哈,[哈哈] 还好俺一直都比较中规中矩,比如api中介绍form表单提交时form.on('submit(filter)',fn)我所有的提交按钮都会带需要的filter,事件也没写过没带filter的所以一直平安无事,但是如果像一些“新手”或者不太循规蹈距的人写成了form.on('submit',fn),提交也能监听到,但是!这个监听就厉害咯,后面的其他form的提交即使你写了filter,提交的时候都会执行一遍可能在其他任意地方定义过的一个没带filter的监听,这个错误是隐性的很难发现。比如下面的
    点击了一次提交,会有两个提交的事件会触发。
    0 回复
  • 0 回复
  • @岁月小偷 大哥我又来烦你了,求助啊
    0 回复
  • @时光的旅行 啥问题,看看会不会
    0 回复
  • maplemei
    2018-5-23
    @岁月小偷 哈, 其实就是在做插件的过程中想到能否更接近的使用layui才发现可以如此处理的.
    0 回复
  • sockball
    2018-5-24
    那直接使用算不算bug呢 调用一次执行2次...
    0 回复
  • @sockball 这个估计没办法一概而论,比如真把他们当成母子事件,那么调用子事件的时候先执行母事件这个是没什么问题的,但是这种情况也可能会引入更多不确定性因素,容易造成一些隐性的异常现象,那么从这个角度来看就是“bug”。所以主要看怎么怎么理解还有如何使用了。我觉得。
    0 回复
  • @岁月小偷 老哥,我又遇到点小问题,我发的那个帖子,死活拿不到值啊
    0 回复
  • jovien
    2018-5-29
    大神又在发表心得了。。。[赞]
    0 回复
  • @jovien [微笑] 大神不敢当啊
    0 回复
  • jovien
    2018-5-29
    @岁月小偷 绝对能当的,对了,还有点事情要找你帮忙,扩展一个方法。
    0 回复
  • @jovien 嘿嘿,有啥问题发出来大家看看哈,我不一定能搞定呢[哈哈]
    0 回复
  • jovien
    2018-5-29
    我有一个提取中文拼音的扩展方法,在输入中文汉字的时候将汉字的拼音提取出来。
    但这里发不来,字数超了
    0 回复