详解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 相关文章推荐
Javascript 二维数组
Nov 26 Javascript
IE与FireFox中的childNodes区别
Oct 20 Javascript
js实现的复制兼容chrome和IE
Apr 03 Javascript
原生javascript实现的分页插件pagenav
Aug 28 Javascript
JavaScript中实现无缝滚动、分享到侧边栏实例代码
Apr 06 Javascript
使用BootStrap实现用户登录界面UI
Aug 10 Javascript
JQuery Ajax WebService传递参数的简单实例
Nov 02 Javascript
jQuery中 $ 符号的冲突问题及解决方案
Nov 04 Javascript
js 中获取制定的cook信息实现方法
Nov 19 Javascript
jquery-file-upload 文件上传带进度条效果
Nov 21 jQuery
在Angular中使用JWT认证方法示例
Sep 10 Javascript
js实现聊天对话框
Feb 08 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
实用函数2
2007/11/08 PHP
Thinkphp模板中使用自定义函数的方法
2012/09/23 PHP
php xml常用函数的集合(比较详细)
2013/06/06 PHP
php mysql实现mysql_select_db选择数据库
2016/12/30 PHP
PHP 99乘法表的几种实现代码
2020/10/13 PHP
使用JavaScript进行进制转换将字符串转换为十进制
2014/09/21 Javascript
JS延时提示框实现方法详解
2015/11/26 Javascript
拥有一个属于自己的javascript表单验证插件
2016/03/24 Javascript
nodeJs爬虫获取数据简单实现代码
2016/03/29 NodeJs
js 能实现监听F5页面刷新子iframe 而父页面不刷新的方法
2016/11/09 Javascript
JS实现的模仿QQ头像资料卡显示与隐藏效果
2017/04/07 Javascript
vue中SPA单页面应用程序详解
2017/11/07 Javascript
javascript实现电脑和手机版样式切换
2017/11/10 Javascript
基于vue中对鼠标划过事件的处理方式详解
2018/08/22 Javascript
vue中当图片地址无效的时候,显示默认图片的方法
2018/09/18 Javascript
flexible.js实现移动端rem适配方案
2020/04/07 Javascript
React Ant Design树形表格的复杂增删改操作
2020/11/02 Javascript
nuxt 实现在其它js文件中使用store的方式
2020/11/05 Javascript
35个Python编程小技巧
2014/04/01 Python
python+tkinter编写电脑桌面放大镜程序实例代码
2018/01/16 Python
Python实现判断一个整数是否为回文数算法示例
2019/03/02 Python
Python2和Python3的共存和切换使用
2019/04/12 Python
基于Python实现人脸自动戴口罩系统
2020/02/06 Python
详解Python 循环嵌套
2020/07/09 Python
pytorch简介
2020/11/11 Python
python字典与json转换的方法总结
2020/12/28 Python
python反编译教程之2048小游戏实例
2021/03/03 Python
美国经典刺绣和字母儿童服装特卖:Smocked Auctions
2018/07/16 全球购物
质检部部长职责
2013/12/16 职场文书
费用会计岗位职责
2014/01/01 职场文书
服装发布会策划方案
2014/05/22 职场文书
2015年爱牙日活动总结
2015/02/05 职场文书
幼儿园教师安全责任书
2015/05/08 职场文书
教你漂亮打印Pandas DataFrames和Series
2021/05/29 Python
JavaWeb实现显示mysql数据库数据
2022/03/19 Java/Android
详解Android中的TimePickerView(时间选择器)的用法
2022/04/30 Java/Android