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 相关文章推荐
jQuery EasyUI API 中文文档 - Calendar日历使用
Oct 19 Javascript
javascript简单性能问题及学习笔记
Feb 04 Javascript
js+html5实现canvas绘制圆形图案的方法
Jun 05 Javascript
jQuery多条件筛选如何实现
Nov 04 Javascript
JS实现页面进入和返回定位到具体位置
Dec 08 Javascript
jquery——九宫格大转盘抽奖实例
Jan 16 Javascript
微信小程序实现tab和swiper切换结合效果
Jul 17 Javascript
详解webpack与SPA实践之开发环境搭建
Dec 18 Javascript
微信小程序有旋转动画效果的音乐组件实例代码
Aug 22 Javascript
用vuex写了一个购物车H5页面的示例代码
Dec 04 Javascript
Vue自定义指令上报Google Analytics事件统计的方法
Feb 25 Javascript
Javascript 类型转换、封闭函数及常见内置对象操作示例
Nov 15 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通过分类列表产生分类树数组的方法
2015/04/20 PHP
PHP中的常见魔术方法功能作用及用法实例
2015/07/01 PHP
PHP实现上传文件并存进数据库的方法
2015/07/16 PHP
Centos PHP 扩展Xchche的安装教程
2016/07/09 PHP
PHP实现笛卡尔积算法的实例讲解
2019/12/22 PHP
JavaScript 基础篇(一)
2012/03/30 Javascript
简介JavaScript中的setHours()方法的使用
2015/06/11 Javascript
angular.bind使用心得
2015/10/26 Javascript
jQuery+CSS3文字跑马灯特效的简单实现
2016/06/25 Javascript
详解Vue 事件驱动和依赖追踪
2017/04/22 Javascript
微信小程序画布圆形进度条显示效果
2020/11/17 Javascript
vue获取元素宽、高、距离左边距离,右,上距离等还有XY坐标轴的方法
2018/09/05 Javascript
基于element-ui组件手动实现单选和上传功能
2018/12/06 Javascript
node实现socket链接与GPRS进行通信的方法
2019/05/20 Javascript
浅谈webpack 四个核心概念之Entry
2019/06/12 Javascript
微信小程序在线客服自动回复功能(基于node)
2019/07/03 Javascript
vue轮播组件实现$children和$parent 附带好用的gif录制工具
2019/09/26 Javascript
JavaScript this指向相关原理及实例解析
2020/07/10 Javascript
[05:46]2018完美盛典-《同梦共竞》
2018/12/17 DOTA
Python3字符串学习教程
2015/08/20 Python
python实现监控某个服务 服务崩溃即发送邮件报告
2018/06/21 Python
python实现在cmd窗口显示彩色文字
2019/06/24 Python
如何使用python把ppt转换成pdf
2019/06/29 Python
使用tensorboard可视化loss和acc的实例
2020/01/21 Python
利用Tensorboard绘制网络识别准确率和loss曲线实例
2020/02/15 Python
python GUI库图形界面开发之PyQt5图片显示控件QPixmap详细使用方法与实例
2020/02/27 Python
傲盾软件面试题
2015/08/17 面试题
商业计算机应用专业自荐书
2014/06/09 职场文书
中职招生先进个人材料
2014/08/31 职场文书
秦始皇兵马俑导游词
2015/02/02 职场文书
大学考试作弊检讨书
2015/05/06 职场文书
2015年教师节主持词
2015/07/03 职场文书
小学2016年第十八届推普周活动总结
2016/04/05 职场文书
经典励志格言:每日一句,让你每天充满能量
2019/08/16 职场文书
Nginx反爬虫策略,防止UA抓取网站
2021/03/31 Servers
Pandas加速代码之避免使用for循环
2021/05/30 Python