JavaScript 自定义事件之我见


Posted in Javascript onSeptember 25, 2017

事件

技术一般水平有限,有什么错的地方,望大家指正。

事件就是用户和浏览器交互的一种途径。假如一个用户注册的功能,我们在填写完基本信息之后,点击提交按钮就可以实现注册功能,要想完成这个功能所需要的就是点击事件。我们预先定义好操作行为,在用户点击提交按钮时就执行我们预先定好的行为,在本例中我们的代码逻辑一般就是收集用户填写信息,验证信息合法性,利用AJAX与服务器交互。

这个过程就好像我们平时封装函数然后调用函数一样,事件其实也就类似函数定义函数调用这样的一个过程,只不过事件函数的调用是由用户的一些操作来告知浏览器,让浏览器在去调用函数的。

首先浏览器已经给我们提供了一列的事件,包括click,keydown等等,为什么还需要自定义事件呢?其实就是对我们的行为进行更准确的描述。以上面的用户注册为例我们可以定义一个名为saveMessage的事件,在点击提交按钮时触发这个事件,好像看起来更加直观一些,不过这看起来和普通的函数调用没什么区别,仔细想了想函数调用和事件触发的区别就是由我们自己执行的函数就是函数调用,不是由我们执行的函数就是事件触发。看下面的代码:

window.onload = function(){
 var demo = document.getElementById("demo");
 demo.onclick = handler;
 function handler(){
  console.log("aaa");
 }
}

在我们点击按钮的时候就会打印aaa,而且很明显的可以看出函数并不是由我们调用的而是由浏览器来执行的,如果我们直接调用函数handler()一样可以打印aaa但这是由我们调用的所以是函数调用。

自定义事件的作用

自定义事件就是我们按照浏览器对事件的机制来自定义的函数。自定义事件,可以对我们的处理函数带来更好的说明,也可以为我们的插件带来更好的处理流程。假如我们又一个这样的需求:从服务器端拉取一组数据然后在HTML中显示成列表,然后标识出第一条数据,假如我们利用一个现有的处理函数,我们可能会这样来写:

dataTable("url");
$("table").find("input[type='checkbox']:first").prop("checked",true);

 这是不能达到我们目的的因为JS是单线程的而AJAX是异步的,当代码$("table").find("input[type='checkbox']:first").prop("checked",true)执行的时候,我们需要的数据还没有获取到。我们去修改插件的内部实现显然是不明智的,一个可以被人接受的插件必然是有合理的回调函数(或者自定义事件)的,假如现在有一个列表绘制成功的回调函数,我们就可以把这个回调函数看做是一个事件,我们可以对这个事件添加事件操作,定义好处理函数,然后在列表绘制成功时让插件来执行这个处理函数。

自定义事件实现

我们模拟浏览器原生的事件来实现自定义事件(en:自定义事件名称,fn:事件处理函数,addEvent:为DOM元素添加自定义事件,triggerEvent:触发自定义事件):

window.onload = function(){
 var demo = document.getElementById("demo");
 demo.addEvent("test",function(){console.log("handler1")});
 demo.addEvent("test",function(){console.log("handler2")});
 demo.onclick = function(){
  this.triggerEvent("test");
 }
}
Element.prototype.addEvent = function(en,fn){
 this.pools = this.pools || {};
 if(en in this.pools){
  this.pools[en].push(fn);
 }else{
  this.pools[en] = [];
  this.pools[en].push(fn);
 }
}
Element.prototype.triggerEvent = function(en){
 if(en in this.pools){
  var fns = this.pools[en];
  for(var i=0,il=fns.length;i<il;i++){
   fns[i]();
  }
 }else{
  return;
 }
}

由我们自己执行的函数是函数调用,非我们执行的函数我们可以叫做触发事件,既然函数不是由我们调用的,那么调用者怎样知道调用哪些函数就是一个问题了,所以就需要在添加事件函数和触发事件函数之间加上一些约束了,那就是两者之间有一个都能访问到的事件池,添加事件时把事件及对应的处理函数放在这个池子里,当满足触发条件时就去池子里找到要触发的事件,执行对应的处理函数,所以就有了我们上面的那一段代码。

对同一个功能(事件)可能有很多个处理函数,所以我们就需要一个集合去存储这些处理函数,这时我们应该反映出两个方案JSON或者数组,JSON的结构是key:value,对于处理函数来说名字是没有什么作用的所以我们用数组来保存处理函数,这组函数是处理什么功能的,所以我们还需要对这组处理函数由一个说明这时候就需要JSON了-->{eventName:[]}。

以简化的BootStrap模态窗来演示自定义事件的作用:

window.onload = function(){
 var show = document.getElementById("show");
 var hide = document.getElementById("hide");
 var content = document.getElementById("content");
 show.onclick = function(){
  content.modal("show");
 }
 hide.onclick = function(){
  content.modal("hide");
 }
 content.addEvent("show",function(){alert("show before")});
 content.addEvent("shown",function(){
  document.getElementById("input").focus();
  alert("show after");
 }); 
}
;(function(ep){
 ep.addEvent = function(en,fn){
  this.pools = this.pools || {};
  if(en in this.pools){
   this.pools[en].push(fn);
  }else{
   this.pools[en] = [];
   this.pools[en].push(fn);
  }
 }
 ep.triggerEvent = function(en){
  if(en in this.pools){
   var fns = this.pools[en];
   for(var i=0,il=fns.length;i<il;i++){
    fns[i]();
   }
  }else{
   return;
  }
 }
 ep.modal = function(t){
  switch(t){
   case "show":
    this.triggerEvent("show");
    this.style.display = "block";
    setTimeout(function(){this.triggerEvent("shown")}.bind(this),0);//该定时器主要是为了在视觉上先看见content,在弹出消息
    break;
   case "hide":
    this.style.display = "none";
    break;
   default:
    break;
  }
 }

}(Element.prototype));

我们可以预先定义好在弹窗出现之前和出现之后的处理函数,当弹窗触发对应事件的时候就执行对应的处理函数。

Javascript 相关文章推荐
js 编写规范
Mar 03 Javascript
jQueryUI如何自定义组件实现代码
Nov 14 Javascript
JS模块与命名空间的介绍
Mar 22 Javascript
判断JS对象是否拥有某种属性的两种方式
Dec 02 Javascript
js+html5获取用户地理位置信息并在Google地图上显示的方法
Jun 05 Javascript
浅谈Javascript实现继承的方法
Jul 06 Javascript
jquery中cookie用法实例详解(获取,存储,删除等)
Jan 04 Javascript
移动端Ionic App 资讯上下循环滚动的实现代码(跑马灯效果)
Aug 29 Javascript
JS返回顶部实例代码
Aug 09 Javascript
官方推荐react-navigation的具体使用详解
May 08 Javascript
示例vue 的keep-alive缓存功能的实现
Dec 13 Javascript
Vue实现拖放排序功能的实例代码
Jul 08 Javascript
详解在vue-cli中使用路由
Sep 25 #Javascript
Bootstrap一款超好用的前端框架
Sep 25 #Javascript
vue封装第三方插件并发布到npm的方法
Sep 25 #Javascript
javascript input输入框模糊提示功能的实现
Sep 25 #Javascript
vue-cli中的webpack配置详解
Sep 25 #Javascript
react.js 父子组件数据绑定实时通讯的示例代码
Sep 25 #Javascript
react native与webview通信的示例代码
Sep 25 #Javascript
You might like
php版小黄鸡simsimi聊天机器人接口分享
2014/01/26 PHP
php类的自动加载操作实例详解
2016/09/28 PHP
thinkPHP5实现的查询数据库并返回json数据实例
2017/10/23 PHP
laravel5.6中的外键约束示例
2019/10/23 PHP
jquery绑定原理 简单解析与实现代码分享
2011/09/06 Javascript
简约JS日历控件 实例代码
2013/07/12 Javascript
JS日期和时间选择控件升级版(自写)
2013/08/02 Javascript
文本框中禁止非数字字符输入比如手机号码、邮编
2013/08/19 Javascript
不使用jquery实现js打字效果示例分享
2014/01/19 Javascript
javascript中定义类的方法详解
2015/02/10 Javascript
javascript实现网站加入收藏功能
2015/12/16 Javascript
百度地图给map添加右键菜单(判断是否为marker)
2016/03/04 Javascript
基于JS实现的随机数字抽签实例
2016/12/08 Javascript
Angular中ng-repeat与ul li的多层嵌套重复问题
2017/07/24 Javascript
深入理解基于vue-cli的vuex配置
2017/07/24 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
Vue中img的src是动态渲染时不显示的解决
2019/11/14 Javascript
selenium 反爬虫之跳过淘宝滑块验证功能的实现代码
2020/08/27 Javascript
JavaScript实现点击出现子菜单效果
2021/02/08 Javascript
[45:59]EG vs OG 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
[02:46]完美世界DOTA2联赛PWL DAY4集锦
2020/11/03 DOTA
使用Python发送邮件附件以定时备份MySQL的教程
2015/04/25 Python
Python构建XML树结构的方法示例
2017/06/30 Python
浅谈Python2、Python3相对路径、绝对路径导入方法
2018/06/22 Python
python3多线程知识点总结
2019/09/26 Python
使用matplotlib绘制图例标签中带有公式的图
2019/12/13 Python
Python文本文件的合并操作方法代码实例
2020/03/31 Python
python实现简单学生信息管理系统
2020/04/09 Python
加拿大领先的优质厨具产品在线购物网站:Golda’s Kitchen
2017/11/17 全球购物
Hotter Shoes美国官网:英国最受欢迎的舒适鞋
2018/08/02 全球购物
介绍一下grep命令的使用
2015/06/12 面试题
大学生学习生活的自我评价
2013/11/01 职场文书
信息管理应届生求职信
2014/03/07 职场文书
《望庐山瀑布》教学反思
2014/04/22 职场文书
煤矿安全协议书
2014/08/20 职场文书
萤火虫之墓观后感
2015/06/05 职场文书