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 页面 Mask实现代码
Jan 09 Javascript
jQuery插件 tabBox实现代码
Feb 09 Javascript
Tips 带三角可关闭的文字提示
Oct 06 Javascript
jQuery当鼠标悬停时放大图片的效果实例
Jul 03 Javascript
JavaScript将Web页面内容导出到Word及Excel的方法
Feb 13 Javascript
js实现网页收藏功能
Dec 17 Javascript
bootstrap布局中input输入框右侧图标点击功能
May 16 Javascript
jQuery插入节点和移动节点用法示例(insertAfter、insertBefore方法)
Sep 08 Javascript
ExtJS 4.2 Grid组件单元格合并的方法
Oct 12 Javascript
JS正则替换去空格的方法
Mar 24 Javascript
详解VUE的状态控制与延时加载刷新
Mar 27 Javascript
React Native预设占位placeholder的使用
Sep 28 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来处理多个提交任务
2006/10/09 PHP
别人整理的服务器变量:$_SERVER
2006/10/20 PHP
PHP下escape解码函数的实现方法
2010/08/08 PHP
PHP生成图片验证码、点击切换实例
2014/06/25 PHP
浅谈PHP链表数据结构(单链表)
2016/06/08 PHP
Yii2中设置与获取别名的函数(setAlias和getAlias)用法分析
2016/07/25 PHP
浅谈PHP错误类型及屏蔽方法
2017/05/27 PHP
Nodejs学习笔记之NET模块
2015/01/13 NodeJs
JavaScript获取页面中表单(form)数量的方法
2015/04/03 Javascript
JavaScript类继承及实例化的方法
2015/07/25 Javascript
Bootstrap自动适应PC、平板、手机的Bootstrap栅格系统
2016/05/27 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
深入理解vue $refs的基本用法
2017/07/13 Javascript
浅入深出Vue之组件使用
2019/07/11 Javascript
基于VUE的v-charts的曲线显示功能
2019/10/01 Javascript
JS实现简单随机3D骰子
2019/10/24 Javascript
对python中类的继承与方法重写介绍
2019/01/20 Python
Python 实现两个服务器之间文件的上传方法
2019/02/13 Python
python序列化与数据持久化实例详解
2019/12/20 Python
python十进制转二进制的详解
2020/02/07 Python
详解Ubuntu环境下部署Django+uwsgi+nginx总结
2020/04/02 Python
pandas 强制类型转换 df.astype实例
2020/04/09 Python
keras 模型参数,模型保存,中间结果输出操作
2020/07/06 Python
Pycharm Git 设置方法
2020/09/15 Python
Canvas获取视频第一帧缩略图的实现
2020/11/11 HTML / CSS
草莓网化妆品澳大利亚站:Strawberrynet AU
2017/12/18 全球购物
《最后的姿势》教学反思
2014/02/27 职场文书
信息技术毕业生自荐信范文
2014/03/13 职场文书
二手房购房意向书范本
2014/04/01 职场文书
小学生环保标语
2014/06/13 职场文书
律政俏佳人观后感
2015/06/09 职场文书
画展观后感
2015/06/17 职场文书
初一年级组工作总结
2015/08/12 职场文书
《爬天都峰》教学反思
2016/02/23 职场文书
vue使用节流函数的踩坑实例指南
2021/05/20 Vue.js
python实现简易自习室座位预约系统
2021/06/30 Python