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实现拖动示例代码
Nov 01 Javascript
函数式 JavaScript(一)简介
Jul 07 Javascript
基于jQuery实现美观且实用的倒计时实例代码
Dec 30 Javascript
BootStrap Fileinput初始化时的一些参数
Dec 30 Javascript
jQuery实现选项卡功能(两种方法)
Mar 08 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
Apr 19 Javascript
Vue使用枚举类型实现HTML下拉框步骤详解
Feb 05 Javascript
nuxt框架中路由鉴权之Koa和Session的用法
May 09 Javascript
vue 中的动态传参和query传参操作
Nov 09 Javascript
详解Js模块化的作用原理和方案
Apr 29 Javascript
vue ant design 封装弹窗表单的使用
Jun 01 Vue.js
JS实现简单九宫格抽奖
Jun 28 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学习 函数 课件
2008/06/15 PHP
一道求$b相对于$a的相对路径的php代码
2010/08/08 PHP
在WordPress中实现评论头像的自定义默认和延迟加载
2015/11/24 PHP
Laravel5.4框架中视图共享数据的方法详解
2019/09/05 PHP
javascript据option的value值快速设定初始的selected选项
2007/08/13 Javascript
js实现的真正的iframe高度自适应(兼容IE,FF,Opera)
2010/03/07 Javascript
javascript解决innerText浏览器兼容问题思路代码
2013/05/17 Javascript
javascript-表格排序(降序/反序)实现介绍(附图)
2013/05/30 Javascript
JS注释所产生的bug 即使注释也会执行
2013/11/19 Javascript
ExtJS4给Combobox设置列表中的默认值示例
2014/05/02 Javascript
js实现对table动态添加、删除和更新的方法
2015/02/10 Javascript
如何减少浏览器的reflow和repaint
2015/02/26 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
canvas的神奇用法
2017/02/03 Javascript
浅谈react.js 之 批量添加与删除功能
2017/04/17 Javascript
Centos6.8下Node.js安装教程
2017/05/12 Javascript
ES6解构赋值实例详解
2017/10/31 Javascript
web前端vue filter 过滤器
2018/01/12 Javascript
解决Vue中引入swiper,在数据渲染的时候,发生不滑动的问题
2018/09/27 Javascript
详解Vue 项目中的几个实用组件(ts)
2019/10/29 Javascript
JavaScript禁止右击保存图片,禁止拖拽图片的实现代码
2020/04/28 Javascript
jQuery中event.target和this的区别详解
2020/08/13 jQuery
[50:04]DOTA2上海特级锦标赛D组小组赛#2 Liquid VS VP第二局
2016/02/28 DOTA
Python实现提取文章摘要的方法
2015/04/21 Python
python生成以及打开json、csv和txt文件的实例
2018/11/16 Python
Flask配置Cors跨域的实现
2019/07/12 Python
python对XML文件的操作实现代码
2020/03/27 Python
鼠标滚轮事件和Mac触控板双指事件
2019/12/23 HTML / CSS
世界上最大的餐具公司:Oneida
2016/12/17 全球购物
Farnell德国:电子元器件供应商
2018/07/10 全球购物
印度第一网上礼品店:IGP.com
2020/02/06 全球购物
关于环保的建议书
2014/05/12 职场文书
终止解除劳动合同证明书
2015/06/17 职场文书
《思路决定出路》读后感3篇
2019/12/11 职场文书
SONY AN-LP1 短波有源天线放大器
2021/04/22 无线电
告别网页搜索!教你用python实现一款属于自己的翻译词典软件
2021/06/03 Python