JavaScript绑定事件监听函数的通用方法


Posted in Javascript onMay 14, 2016

事件绑定的3中常用方法:传统绑定、W3C绑定方法、IE绑定方法。但是,在实际开发中对于我们来讲重要的是需要一个通用的、跨浏览器的绑定方法。如果我们在互联网上搜索一下会发现许多方法,以下是比较知名的几种方法:

在开始学期下面几种方法之前,应当讨论一下,一个好的addEvent()方法应当达到哪些要求:

a、支持同一元素的同一事件句柄可以绑定多个监听函数;

b、如果在同一元素的同一事件句柄上多次注册同一函数,那么第一次注册后的所有注册都被忽略;

c、函数体内的this指向的应当是正在处理事件的节点(如当前正在运行事件句柄的节点);

d、监听函数的执行顺序应当是按照绑定的顺序执行;

e、在函数体内不用使用 event = event || window.event; 来标准化Event对象;

一、John Resig 所写的 addEvent() 函数

function addEvent( obj, type, fn ) {
  if ( obj.attachEvent ) {
   obj['e'+type+fn] = fn;
   obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
   obj.attachEvent( 'on'+type, obj[type+fn] );
  } else
   obj.addEventListener( type, fn, false );
 }
 function removeEvent( obj, type, fn ) {
  if ( obj.detachEvent ) {
   obj.detachEvent( 'on'+type, obj[type+fn] );
   obj[type+fn] = null;
  } else
   obj.removeEventListener( type, fn, false );
 }

这个函数如此简单易懂,的确非常令人惊讶。那么我们还是要看看上面的五点要求:

对于第一点满足了;

对于第三点和第五点,肯定也满足了;

对于第二点,并没有满足,因为addEventListener()会忽略重复注册,而attachEvent()则不会忽略;

但是第四点,并没有满足,因为Dom标准没有确定调用一个对象的时间处理函数的顺序,所以不应该想当然的认为它们以注册的顺序调用。

但是这个函数仍然是一个非常优秀的函数。

二、Dean Edward 所写的 addEvent() 函数

function addEvent(element, type, handler) {
 if (!handler.$$guid) handler.$$guid = addEvent.guid++;
 if (!element.events) element.events = {};
  var handlers = element.events[type];
 if (!handlers) {
  handlers = element.events[type] = {};
  if (element["on" + type]) {
   handlers[0] = element["on" + type];
  }
 }
 handlers[handler.$$guid] = handler;
 element["on" + type] = handleEvent;
}

addEvent.guid = 1;
 
function removeEvent(element, type, handler) {
 if (element.events && element.events[type]) {
  delete element.events[type][handler.$$guid];
 }
}
function handleEvent(event) {
 var returnValue = true;
 event = event || fixEvent(window.event);
 var handlers = this.events[event.type];
 for (var i in handlers) {
  this.$$handleEvent = handlers[i];
  if (this.$$handleEvent(event) === false) {
   returnValue = false;
  }
 }
 return returnValue;
};
 
function fixEvent(event) {
 event.preventDefault = fixEvent.preventDefault;
 event.stopPropagation = fixEvent.stopPropagation;
 return event;
};
fixEvent.preventDefault = function() {
 this.returnValue = false;
};
fixEvent.stopPropagation = function() {
 this.cancelBubble = true;
};

该函数使用了传统的绑定方法,所以它可以在所有的浏览器中工作,也不会造成内存泄露。

但是对于最初提出的5点,该函数只是满足了前四点。只有最后一点没有满足,因为在JavaScript中对for/in语句的执行顺序没有规定是按照赋值的顺序执行,尽管大部分时刻是按照预期的顺序执行,因此在不同的JavaScript版本或实现中这一语句的顺序有可能不同。

三、Dean Edward 的 addEvent() 函数的改进

Array.prototype.indexOf = function( obj ){
 var result = -1 , length = this.length , i=length - 1;
 for ( ; i>=0 ; i-- ) {
  if ( this[i] == obj ) {
   result = i;
   break;
  }
 }
 return result;
}
Array.prototype.contains = function( obj ) {
 return ( this.indexOf( obj ) >=0 )
}
Array.prototype.append = function( obj , nodup ) {
 if ( !(nodup && this.contains( obj )) ) {
  this[this.length] = obj;
 }
}
Array.prototype.remove = function( obj ) {
 var index = this.indexOf( obj );
 if ( !index ) return ;
 return this.splice( index , 1);
};
function addEvent(element , type , fun){
 if (!element.events) element.events = {};   
 var handlers = element.events[type];
 if (!handlers) {
  handlers = element.events[type] = [];
  if(element['on' + type]) {  
   handlers[0] = element['on' + type];
  }
 }
 handlers.append( fun , true)
 element['on' + type] = handleEvent;
}
function removeEvent(element , type , fun) {
 if (element.events && element.events[type]) {
  element.events[type].remove(fun); 
 }
}
function handleEvent(event) {
 var returnValue = true , i=0;
 event = event || fixEvent(window.event);
 var handlers = this.events[event.type] , length = handlers.length;
 for ( ; i < length ; i++) {
  if ( handlers[i].call( this , event) === false ){
   returnValue = false;
  }
 }
 return returnValue;
}
function fixEvent(event) {
 event.preventDefault = fixEvent.preventDefault;
 event.stopPropagation = fixEvent.stopPropagation;
 return event;
}
fixEvent.preventDefault = function() {
 this.returnValue = false;
};
fixEvent.stopPropagation = function() {
 this.cancelBubble = true;
};

该函数是本人对Dean Edward 的 addEvent() 函数的改进,完全满足了最初提出的5点要求,希望对大家的学习有所帮助,谢谢大家的阅读。

Javascript 相关文章推荐
自动生成文章摘要的代码[JavaScript 版本]
Mar 20 Javascript
编写跨浏览器的javascript代码必备[js多浏览器兼容写法]
Oct 29 Javascript
web性能优化之javascript性能调优
Dec 28 Javascript
总结JavaScript设计模式编程中的享元模式使用
May 21 Javascript
基于webpack-hot-middleware热加载相关错误的解决方法
Feb 22 Javascript
浅析微信扫码登录原理(小结)
Oct 29 Javascript
Element input树型下拉框的实现代码
Dec 21 Javascript
React中阻止事件冒泡的问题详析
Apr 12 Javascript
vue elementUI 表单校验功能之数组多层嵌套
Jun 04 Javascript
Layui给switch添加响应事件的例子
Sep 03 Javascript
layer ui插件显示tips时,修改字体颜色的实现方法
Sep 11 Javascript
在vue中axios设置timeout超时的操作
Sep 04 Javascript
易被忽视的js事件问题总结
May 14 #Javascript
jQuery防止重复绑定事件的解决方法
May 14 #Javascript
jQuery基于扩展简单实现倒计时功能的方法
May 14 #Javascript
jquery动态切换背景图片的简单实现方法
May 14 #Javascript
jQuery基于$.ajax设置移动端click超时处理方法
May 14 #Javascript
jQuery基于扩展实现的倒计时效果
May 14 #Javascript
Angularjs中UI Router的使用方法
May 14 #Javascript
You might like
PHP 采集程序 常用函数
2008/12/18 PHP
利用PHP实现图片等比例放大和缩小的方法详解
2013/06/06 PHP
ucenter通信原理分析
2015/01/09 PHP
WordPress中用于获取搜索表单的PHP函数使用解析
2016/01/05 PHP
利用PHP内置SERVER开启web服务(本地开发使用)
2021/03/09 PHP
Javascript select下拉框操作常用方法
2009/11/09 Javascript
js隐式全局变量造成的bug示例代码
2014/04/22 Javascript
IE6 hack for js 集锦
2014/09/23 Javascript
node中socket.io的事件使用详解
2014/12/15 Javascript
解决bootstrap中modal遇到Esc键无法关闭页面
2015/03/09 Javascript
jQuery选择器源码解读(五):tokenize的解析过程
2015/03/31 Javascript
jQuery网页版打砖块小游戏源码分享
2015/08/20 Javascript
基于Node.js实现nodemailer邮件发送
2016/01/26 Javascript
简单实现js选项卡切换效果
2016/02/03 Javascript
全面解析Javascript无限添加QQ好友原理
2016/06/15 Javascript
Angular6项目打包优化的实现方法
2019/12/15 Javascript
vue实现数据控制视图的原理解析
2020/01/07 Javascript
Vue通过getAction的finally来最大程度避免影响主数据呈现问题
2020/04/24 Javascript
js+canvas实现转盘效果(两个版本)
2020/09/13 Javascript
[48:28]完美世界DOTA2联赛循环赛FTD vs Magma第二场 10月30日
2020/10/31 DOTA
python 中文乱码问题深入分析
2011/03/13 Python
python导入csv文件出现SyntaxError问题分析
2017/12/15 Python
Python字符串的全排列算法实例详解
2019/01/07 Python
Python循环实现n的全排列功能
2019/09/16 Python
python安装virtualenv虚拟环境步骤图文详解
2019/09/18 Python
Python生成器generator原理及用法解析
2020/07/20 Python
HTML 5.1来了 9月份正式发布 更新内容预览
2016/04/26 HTML / CSS
阿拉伯世界最大的电子卖场:Souq埃及
2016/08/01 全球购物
Gweniss格温妮丝女包官网:英国纯手工制造潮流包包品牌
2018/02/07 全球购物
Farfetch阿联酋:奢侈品牌时尚购物平台
2019/07/26 全球购物
莫斯科大型旅游休闲商品超市:Camping.ru
2020/09/16 全球购物
婚纱摄影师求职信
2014/03/07 职场文书
交通安全责任书范本
2014/07/24 职场文书
普通党员对照检查材料
2014/08/28 职场文书
避暑山庄导游词
2015/02/04 职场文书
go设置多个GOPATH的方式
2021/05/05 Golang