详解javascript实现自定义事件


Posted in Javascript onJanuary 19, 2016

我们平时在操作dom时候经常会用到onclick,onmouseover等一系列浏览器特定行为的事件,
那么自定义事件,顾名思义,就是自己定义事件类型,自己定义事件处理函数,在合适的时候需要哪个事件类型,就去调用哪个处理程序

1.js所支持的浏览器默认事件

浏览器特定行为的事件,或者叫系统事件,js默认事件等等都行,大家知道我指的什么就行,下文我叫他js默认事件。
js默认事件的事件绑定,事件移出等一系列操作,相信大家都有用到过,如:

//DOM0级事件处理程序
var oDiv = document.getElementById('oDiv');
oDiv.onclick = function(){
  alert("你点击了我");
}

又或者

//DOM2级事件处理程序
var oDiv = document.getElementById('oDiv');

//非ie
oDiv.addEventListener("click",function(){
  alert("你点击了我");
},false); 

//ie
oDiv.attachEvent("onclick", function(){
  alert("你点击了我");
});

所有我就不做过多的研究,毕竟我们来讨论js自定义事件,这里给出一个我之前封装过的处理js默认事件的代码:

//跨浏览器的事件处理程序 
//调用时候直接用domEvent.addEvent( , , );直接调用 
//使用时候,先用addEvent添加事件,然后在handleFun里面直接写其他函数方法,如getEvent; 
//addEventListener和attachEvent---都是dom2级事件处理程序 
var domEvent = { 
  //element:dom对象,event:待处理的事件,handleFun:处理函数 
  //事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等 
  addEvent:function(element,event,handleFun){ 
    //addEventListener----应用于mozilla 
    if(element.addEventListener){ 
      element.addEventListener(event,handleFun,false); 
    }//attachEvent----应用于IE 
    else if(element.attachEvent){ 
      element.attachEvent("on"+event,handleFun); 
    }//其他的选择dom0级事件处理程序 
    else{ 
      //element.onclick===element["on"+event]; 
      element["on"+event] = handleFun; 
    } 
  }, 
  //事件名称,含“on”,比如“onclick”、“onmouseover”、“onkeydown”等 
  removeEvent:function(element,event,handleFun){ 
    //removeEventListener----应用于mozilla 
    if (element.removeEventListener) { 
      element.removeEventListener(event,handleFun,false); 
    }//detachEvent----应用于IE 
    else if (element.detachEvent) { 
      element.detachEvent("on"+event,handleFun); 
    }//其他的选择dom0级事件处理程序 
    else { 
      element["on"+event] = null; 
    } 
  }, 
  //阻止事件冒泡 
  stopPropagation:function(event){ 
    if(event.stopPropagation){ 
      event.stopPropagation(); 
    }else{ 
      event.cancelBubble = true;//IE阻止事件冒泡,true代表阻止 
    } 
  }, 
  //阻止事件默认行为 
  preventDefault:function(event){ 
    if(event.preventDefault){ 
      event.preventDefault(); 
    }else{ 
      event.returnValue = false;//IE阻止事件冒泡,false代表阻止 
    } 
  }, 
  //获得事件元素 
  //event.target--非IE 
  //event.srcElement--IE 
  getElement:function(event){ 
    return event.target || event.srcElement; 
  }, 
  //获得事件 
  getEvent:function(event){ 
    return event? event : window.event; 
  }, 
  //获得事件类型 
  getType:function(event){ 
    return event.type; 
  } 
};

接下类我们不如正题,js自定义事件

2.对象直接量封装js自定义事件

根据上面的封装,我们可以这样构思

var eventTarget = {
  addEvent: function(){
    //添加事件
  },
  fireEvent: function(){
    //触发事件
  },
  removeEvent: function(){
    //移除事件
  }
};

相信这样大家还是比较好理解的,然后又有一个问题大家可以想到,那就是,js默认事件,js可以一一对应,知道那个是那个,那么我们的自定义事件呢,这个一一对应的映射表只能我们自己去建立,然后我这样

var eventTarget = {
  //保存映射
  handlers:{},
  addEvent: function(){
    //处理代码
  },
  fireEvent: function(){
    //触发代码
  },
  removeEvent: function(){
    //移出代码
  }
};

我是这样构建这个映射关系的

handlers = {
  "type1":[
    "fun1",
    "fun2",
    // "..."
  ],
  "type2":[
    "fun1",
    "fun2"
    // "..."
  ]
  //"..."
}

这样每一个类型可以有多个处理函数,以便于我们以后扩充
接下来就是代码方面的实战的,编写具体的处理代码了…

相信大家对于这个思路已经很清楚了,我直接附上代码

//直接量处理js自定义事件
var eventTarget = {
  //保存事件类型,处理函数数组映射
  handlers:{},
  //注册给定类型的事件处理程序,
  //type -> 自定义事件类型, handler -> 自定义事件回调函数
  addEvent: function(type, handler){
    //判断事件处理数组是否有该类型事件
    if(eventTarget.handlers[type] == undefined){
      eventTarget.handlers[type] = [];
    }
    //将处理事件push到事件处理数组里面
    eventTarget.handlers[type].push(handler);
  },
  //触发一个事件
  //event -> 为一个js对象,属性中至少包含type属性,
  //因为类型是必须的,其次可以传一些处理函数需要的其他变量参数。(这也是为什么要传js对象的原因)
  fireEvent: function(event){
    //判断是否存在该事件类型
    if(eventTarget.handlers[event.type] instanceof Array){
      var _handler = eventTarget.handlers[event.type];
      //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
      for(var i = 0; i < _handler.length; i++){
        //执行触发
        _handler[i](event);
      }
    }
  },
  //注销事件
  //type -> 自定义事件类型, handler -> 自定义事件回调函数
  removeEvent: function(type, handler){
    if(eventTarget.handlers[type] instanceof Array){
      var _handler = eventTarget.handlers[type];
      //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
      for(var i = 0; i < _handler.length; i++){
        //找出本次需要处理的事件下标
        if(_handler[i] == handler){
          break;
        }
      }
      //删除处理事件
      _handler.splice(i, 1);
    }
  }
};

这是一种调用运行的方法

eventTarget.addEvent("eat",function(){
  console.log(123);  //123
});
eventTarget.fireEvent({type: "eat"});

这种方法有一个缺点,不能删除该处理事件,因为我们是用映射表做的,而且也不提倡,直接给映射表里面存这么多数据,有点多。

另一种方法,将处理事件提取出来(推荐)

function b(){
   console.log(123);
}
eventTarget.addEvent("eat",b);
eventTarget.fireEvent({
  type: "eat"
});                   //123
eventTarget.removeEvent("eat",b);
eventTarget.fireEvent({type: "eat"});  //空

也可以这样,传递更多的参数

eventTarget.fireEvent({
  type: "eat",
  food: "banana"
}); 
function b(data){
   console.log(data.food); //banana
}

总结:字面量这种方法,有点儿缺点,就是万一一不小心,把某个属性在handler函数里面,赋值null,这样会造成我们的的eventTarget 方法崩盘。看来原型应该是个好方法,更安全一点。

3.对象原型封装js自定义事件

由于前面思路基本都讲清楚了,这里我直接附上代码,大家可以研究下其中的利弊,或许你可以找到更好的方法解决Ta…

//自定义事件构造函数
function EventTarget(){
  //事件处理程序数组集合
  this.handlers = {};
}
//自定义事件的原型对象
EventTarget.prototype = {
  //设置原型构造函数链
  constructor: EventTarget,
  //注册给定类型的事件处理程序,
  //type -> 自定义事件类型, handler -> 自定义事件回调函数
  addEvent: function(type, handler){
    //判断事件处理数组是否有该类型事件
    if(typeof this.handlers[type] == 'undefined'){
      this.handlers[type] = [];
    }
    //将处理事件push到事件处理数组里面
    this.handlers[type].push(handler);
  },
  //触发一个事件
  //event -> 为一个js对象,属性中至少包含type属性,
  //因为类型是必须的,其次可以传一些处理函数需要的其他变量参数。(这也是为什么要传js对象的原因)
  fireEvent: function(event){
    //模拟真实事件的event
    if(!event.target){
      event.target = this;
    }
    //判断是否存在该事件类型
    if(this.handlers[event.type] instanceof Array){
      var handlers = this.handlers[event.type];
      //在同一个事件类型下的可能存在多种处理事件,找出本次需要处理的事件
      for(var i = 0; i < handlers.length; i++){
        //执行触发
        handlers[i](event);
      }
    }
  },
  //注销事件
  //type -> 自定义事件类型, handler -> 自定义事件回调函数
  removeEvent: function(type, handler){
    //判断是否存在该事件类型
    if(this.handlers[type] instanceof Array){
      var handlers = this.handlers[type];
      //在同一个事件类型下的可能存在多种处理事件
      for(var i = 0; i < handlers.length; i++){
        //找出本次需要处理的事件下标
        if(handlers[i] == handler){
          break;
        }
      }
      //从事件处理数组里面删除
      handlers.splice(i, 1);
    }
  }
};

调用方法

function b(){
  console.log(123);
}

var target = new EventTarget();
target.addEvent("eat", b);

target.fireEvent({
  type: "eat"
});                 //123

原型这种方法,与直接量方法功能是一样的…

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
js replace 与replaceall实例用法详解
Aug 03 Javascript
Javascript控制页面链接在新窗口打开具体方法
Aug 16 Javascript
js输入框邮箱自动提示功能代码实现
Dec 10 Javascript
学习Javascript面向对象编程之封装
Feb 23 Javascript
探讨:JavaScript ECAMScript5 新特性之get/set访问器
May 05 Javascript
微信小程序实现点击按钮修改字体颜色功能【附demo源码下载】
Dec 05 Javascript
小程序视频或音频自定义可拖拽进度条的示例代码
Sep 30 Javascript
Node.js net模块功能及事件监听用法分析
Jan 05 Javascript
微信小程序如何修改本地缓存key中单个数据的详解
Apr 26 Javascript
jQuery鼠标滑过横向时间轴样式(代码详解)
Nov 01 jQuery
JS基础之逻辑结构与循环操作示例
Jan 19 Javascript
Webpack中SplitChunksPlugin 配置参数详解
Mar 24 Javascript
JS拖拽组件学习使用
Jan 19 #Javascript
理解JS绑定事件
Jan 19 #Javascript
AngularJS模块学习之Anchor Scroll
Jan 19 #Javascript
jQuery unbind()方法实例详解
Jan 19 #Javascript
jQuery绑定事件监听bind和移除事件监听unbind用法实例详解
Jan 19 #Javascript
详解JavaScript对象序列化
Jan 19 #Javascript
学习JavaScript设计模式之单例模式
Jan 19 #Javascript
You might like
php 全局变量范围分析
2009/08/07 PHP
PHP 远程关机实现代码
2009/11/10 PHP
Laravel 5框架学习之Blade 简介
2015/04/08 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
javascript appendChild,innerHTML,join性能比较代码
2009/08/29 Javascript
JavaScript实现统计文本框Textarea字数增强用户体验
2012/12/21 Javascript
javascript创建createXmlHttpRequest对象示例代码
2014/02/10 Javascript
jQuery元素选择器用法实例
2014/12/23 Javascript
JavaScript判断undefined类型的正确方法
2015/06/30 Javascript
纯前端JavaScript实现Excel IO案例分享
2016/08/26 Javascript
JS验证图片格式和大小并预览的简单实例
2016/10/11 Javascript
基于Bootstrap的Java开发问题汇总(Spring MVC)
2017/01/15 Javascript
微信小程序 template模板详解及实例代码
2017/03/09 Javascript
Vue 组件传值几种常用方法【总结】
2018/05/28 Javascript
React key值的作用和使用详解
2018/08/23 Javascript
微信小程序页面缩放式侧滑效果的实现代码
2018/11/15 Javascript
你所不知道的Python奇技淫巧13招【实用】
2016/12/14 Python
Python实现京东秒杀功能代码
2019/05/16 Python
Python实现随机取一个矩阵数组的某几行
2019/11/26 Python
python基于opencv检测程序运行效率
2019/12/28 Python
Python 自由定制表格的实现示例
2020/03/20 Python
python3环境搭建过程(利用Anaconda+pycharm)完整版
2020/08/19 Python
一些关于python 装饰器的个人理解
2020/08/31 Python
python中yield的用法详解
2021/01/13 Python
关于h5中的fetch方法解读(小结)
2017/11/15 HTML / CSS
阿迪达斯越南官网:adidas越南
2020/07/19 全球购物
Linux如何修改文件和文件夹的权限
2013/09/05 面试题
JSF的标签库有哪些
2012/04/27 面试题
财务会计应届生求职信
2013/11/24 职场文书
我的网上商城创业计划书
2013/12/26 职场文书
基层干部十八大感言
2014/01/19 职场文书
岗位职责的构建方法
2014/02/01 职场文书
毕业生自荐信格式
2014/03/07 职场文书
防灾减灾宣传标语
2014/10/07 职场文书
2019年怎样才能撰写出优秀的自荐信
2019/03/25 职场文书
教你使用Ubuntu搭建DNS服务器
2022/09/23 Servers