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 相关文章推荐
通过event对象的fromElement属性解决热区设置主实体的一个bug
Dec 22 Javascript
jQuery制作简单柱状图实例
Jan 28 Javascript
js实现简单的验证码
Dec 25 Javascript
JavaScript常用数组算法小结
Feb 13 Javascript
jquery文字填写自动高度的实现方法
Nov 07 Javascript
JavaScript简单生成 N~M 之间随机数的方法
Jan 13 Javascript
妙用Angularjs实现表格按指定列排序
Jun 23 Javascript
利用10行js代码实现上下滚动公告效果
Dec 08 Javascript
Angular8路由守卫原理和使用方法
Aug 29 Javascript
JS Generator 函数的含义与用法实例总结
Apr 08 Javascript
vue中后端做Excel导出功能返回数据流前端的处理操作
Sep 08 Javascript
Vue封装Axios请求和拦截器的步骤
Sep 16 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
别人整理的服务器变量:$_SERVER
2006/10/20 PHP
php 获取select下拉列表框的值
2010/05/08 PHP
php数组生成html下拉列表的方法
2015/07/20 PHP
PHP实现的常规正则验证helper公共类完整实例
2017/04/27 PHP
IE浏览器打印的页眉页脚设置解决方法
2009/12/08 Javascript
JavaScript中几个重要的属性(this、constructor、prototype)介绍
2013/05/19 Javascript
node.js中的http.response.getHeader方法使用说明
2014/12/14 Javascript
jQuery中[attribute=value]选择器用法实例
2014/12/31 Javascript
在JavaScript中如何解决用execCommand(
2015/10/19 Javascript
用jQuery实现优酷首页轮播图
2017/01/09 Javascript
async/await与promise(nodejs中的异步操作问题)
2017/03/03 NodeJs
Webpack实战加载SVG的方法
2017/12/26 Javascript
echarts实现词云自定义形状的示例代码
2019/02/20 Javascript
纯异步nodejs文件夹(目录)复制功能
2019/09/03 NodeJs
Vue 事件的$event参数=事件的值案例
2021/01/29 Vue.js
Python自定义scrapy中间模块避免重复采集的方法
2015/04/07 Python
Python利用openpyxl库遍历Sheet的实例
2018/05/03 Python
使用Python的toolz库开始函数式编程的方法
2018/11/15 Python
使用Python正则表达式操作文本数据的方法
2019/05/14 Python
Python爬虫学习之获取指定网页源码
2019/07/30 Python
基于 Django 的手机管理系统实现过程详解
2019/08/16 Python
python异步编程 使用yield from过程解析
2019/09/25 Python
Pytorch中实现只导入部分模型参数的方式
2020/01/02 Python
Python paramiko 模块浅谈与SSH主要功能模拟解析
2020/02/29 Python
详解python UDP 编程
2020/08/24 Python
css3 iphone玻璃透明气泡完美实现
2013/03/20 HTML / CSS
Fanatics法国官网:美国体育电商
2019/08/27 全球购物
澳大利亚领先的时尚内衣零售商:Bras N Things
2020/07/28 全球购物
华为慧通面试题
2012/09/11 面试题
剪枝的学问教学反思
2014/02/07 职场文书
信息总监管理职责范本
2014/03/08 职场文书
装配出错检讨书
2014/09/23 职场文书
婚前财产协议书范本
2014/10/19 职场文书
工程部经理岗位职责
2015/02/02 职场文书
简短清晨问候语
2015/11/10 职场文书
html5调用摄像头实例代码
2021/06/28 HTML / CSS