当我们编写脚本的时候创建了交叉引用,例如如下代码:

window.onload = function() {

var x = document.getElementsByTagName('H3');

for (var i = 0; i < x.length; i++) {

x[i].onclick = openClose;

x[i].relatedElement = x[i].nextSibling; // simplified situation

x[i].relatedElement.relatedElement = x[i];

}

}

或者在函数中使用脚本语言最常见的闭句Closures的时候,IE都无法回收内存。而闭句在给DOM对象注册事件处理器(event handler)的时候最为常用。Novemberborn提供了一些example可以让你运行并切实感受到这个bug。

我最喜爱的QuirkMode 去年初意识到这个bug存在巨大隐患,觉得有必要呼吁广大web开发者关注并竭力避免这个问题,于是举办了一个慈善邀请赛,鼓励大家提交各自 addEvent/removeEvent 方案。并终于在去年10月下旬宣布了他们认为的胜利者:John Resig,让John赢得胜利的代码如下:

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);

}

QuirkMode 对选择John为胜利者的解释概括来说就是以上代码最简洁有效,在避免内存问题的同时还巧妙的保证了this关键字在ie的attachEvent中能正常工作。缺点当然还是存在:

不支持 Netscape 4 和 Explorer 5 Mac。(有可能国内的程序员会嗤之以鼻,但国外很强调广泛的兼容性)

在 removeEvent 中遗漏了remove obj["e"+type+fn]。

总之不管怎么说,简单取胜。

结果一出,众多参赛与评论者不服气,很快又挑出了John的代码的几处毛病:

addEvent中本身就使用了闭句,所以没有根本解决IE内存泄露的问题。

没有解决同类型的事件可能被重复注册而被IE重复执行的问题。

几个高手于是提出了改进性的方案:

/*

Original idea by John Resig

Tweaked by Scott Andrew LePera, Dean Edwards and Peter-Paul Koch

Fixed for IE by Tino Zijdel (crisp)

Note that in IE this will cause memory leaks and still doesn't quite function the same as in browsers that do support the W3C event model:

- event execution order is not the same (LIFO in IE against FIFO)

- functions attached to the same event on the same element multiple times will also get executed multiple times in IE

*/

function addEvent(obj, type, fn) {

if (obj.addEventListener) obj.addEventListener(type, fn, false);

else if (obj.attachEvent) {

obj["e" + type + fn] = fn;

obj.attachEvent("on" + type,

function() {

obj["e" + type + fn]();

});

}

}

function removeEvent(obj, type, fn) {

if (obj.removeEventListener) obj.removeEventListener(type, fn, false);

else if (obj.detachEvent) {

obj.detachEvent("on" + type, obj["e" + type + fn]);

obj["e" + type + fn] = null;

}

}

很明显,虽然修正了John代码的一些不足。但内存泄露依然存在,部分浏览器依然不支持,还是无法避免ie重复注册。另外根据注释:当在同一个对象上注册多个事件处理器的时候,IE与其他浏览器的执行顺序是不同的,这又是一个隐患。

几天之后,一个被认为最严谨的方案由Dean Edwards 提出。Dean他的方案与众不同:

不执行对象检测(Object detection)

没有调用 addeventListener/attachEvent 方法

保持this关键字的运行于正确的上下文环境

正确传递 event 对象参数

完全跨浏览器至此(包括IE4和NS4)

不存在内存泄露

Dean的代码如下:

// written by Dean Edwards, 2005

// http://dean.edwards.name/function ;addEvent(element, type, handler) {

// assign each event handler a unique ID

// 为事件处理函数设定一个唯一值

if (!handler.$$guid) handler.$$guid = addEvent.guid++;

// create a hash table of event types for the element

if (!element.events) element.events = {};

// create a hash table of event handlers for each element/event pair

var handlers = element.events[type];

if (!handlers) {

handlers = element.events[type] = {};

// store the existing event handler (if there is one)

// 如果对象已经注册有事件处理,那么要保留下来,并保存为第一个

if (element["on" + type]) {

handlers[0] = element["on" + type];

}

}

// store the event handler in the hash table

handlers[handler.$$guid] = handler;

// assign a global event handler to do all the work

// 指派一个全局函数做统一的事件处理,同时避免了反复注册

element["on" + type] = handleEvent;

};

// a counter used to create unique IDs

addEvent.guid = 1;

function removeEvent(element, type, handler) {

// delete the event handler from the hash table

if (element.events && element.events[type]) {

delete element.events[type][handler.$$guid];

}

};

function handleEvent(event) {

// grab the event object (IE uses a global event object)

event = event || window.event;

// get a reference to the hash table of event handlers

// 这里的 this 随 handlerEvent function 被触发的source element 变化而变化

var handlers = this.events[event.type];

// execute each event handler

for (var i in handlers) {

//这样写才能保证注册的事件处理函数中的 this 得到正确的引用,直接handlers[i]()是不行的

this.$$handleEvent = handlers[i];

this.$$handleEvent(event);

}

};

这段代码相比之前就大了不少了,不过确实很精妙。可是这段代码却引入了其他的问题,比如无法处理事件处理函数的返回值,for..in循环可能因为 (Object.prototype)的错误应用而中断等等...很快Dean推出一个"updated version"。

要做到最好真的好辛苦。

目前似乎Dean的最终版本是最全面的解决方案。不过就我个人意见,感觉有些吹毛求疵了。尽量使用浏览器本身的实现和保持简单是我一贯坚持的主张。但洋人这种严谨的态度,还是让我深深敬佩。

java add event handler_最佳的addEvent事件绑定是怎样诞生的相关推荐

  1. 11.①事件冒泡:取消冒泡event.cancelBubble=true或.sopImmediatePropagation()②事件绑定addEventListener(参数1,参2,参3)③事件传播

    目录 一:事件冒泡: 1.冒泡指的是事件的向上传导, 2.冒泡与子元素所在位置无关,与结构有关(就算给子元素添加定位,移出父元素里面,还是会产生冒泡) 3.要取消事件的冒泡需要用到事件对象(两种方法) ...

  2. Vue的数据绑定、Vue的事件绑定、Class和Style的绑定

    一.Vue的数据绑定 1. 单向数据绑定:将Model绑定到View上,当通过JavaScript代码改变了Model时,View就会自动刷新.不需要进行额外的DOM操作就可以实现视图和模型的联动 ​ ...

  3. java 8入门与实践_30个Java入门技巧和最佳实践

    java 8入门与实践 Java是最流行的编程语言之一-无论是Win应用程序,Web应用程序,移动,网络,消费电子产品,机顶盒设备,Java随处可见. 在Java上运行的设备超过30亿. 据Oracl ...

  4. Java 基础【04】Swing 组件事件注册

    聪明出于勤奋,天才在于积累.--华罗庚 对上次的三个问题的个人理解: 1) 程序首先是从main函数开始执行的,假设main 函数不是 static ,就要先实例化这个类,然后调用 main 方法,这 ...

  5. java计算器监听_计算器及事件监听

    import java.awt.*; import java.awt.event.*; import java.util.Date; import javax.swing.*; public clas ...

  6. Java点击按钮div缩放_[Java教程]怎样给div增加resize事件

    [Java教程]怎样给div增加resize事件 0 2016-10-31 11:00:04 当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件,这个事件在window上面触发,那么如 ...

  7. java实用教程——组件及事件处理——DocumentEvent事件

    DocumentEvent事件源: 文本区Document的维护 注册监视器: 使用addDocumentListener(DocumentListener listen)为事件源添加监视器 Docu ...

  8. java实用教程——组件及事件处理——ItemEvent事件(设置字体类型)

    ItemEvent事件源: 选择框,下拉列表都可以触发ItemEvent事件 注册监视器: 能够触发ItemEvent事件的组件使用addItemListener(ItemListener liste ...

  9. java实用教程——组件及事件处理——ActionEvent事件

    事件源: 文本框,按钮,菜单项,密码框,单选按钮 注册监视器: 能够触发ActionEvent事件的组件使用方法 addActionListener(ActionListener listener) ...

最新文章

  1. linux shell let命令,shell编程中的let与(())
  2. 那些还在外包公司干的程序员们,快醒醒吧!
  3. C++ternary search三元搜索的实现算法(附完整源码)
  4. Scanner进阶详细讲解
  5. 同宿舍程序员毕业五年后:有人年薪百万,有人月薪一万
  6. java线程池1001java线程池_深入浅出Java(Android )线程池ThreadPoolExecutor
  7. Java读取文件方法大全
  8. 一、线性表的顺序存储和基本运算
  9. 黄东旭当选 CCF 数据库专业委员会、开源发展委员会、大数据专家委员会执行委员
  10. 如何删除子域信任关系
  11. 《数据结构C语言版》-栈的概念和栈的实现
  12. 最新界面很漂亮的在线工具箱,包含站长工具箱等等功能
  13. 全国计算机等级AJIL,全国计算机二级VB知识点2015第一次课
  14. 2017.12.20 静态网页小实战
  15. 读《why programs fail》的闲话
  16. 信息论的应用例子:数据压缩与信息熵、为什么K线这种技术指标没用了?
  17. 做时间的记录者—手机摄影达人20天养成计划
  18. 完整学习笔记之Android基础(详版)
  19. HTML+CSS期末大作业:保护动物网站设计——大象(6页) 学生DW网页设计作业成品 web课程设计网页规划与设计 大学生动物保护网页作品 环保网页设计作业模板 学生网页制作源代码下载...
  20. 让CocoStudio变成libgdx的UI编辑器

热门文章

  1. JavaSE基础答案合集class篇
  2. oracle rdbms 占CPU,求助,数据库某个进程cpu占用高
  3. meego linux版本,记MeeGo的多系统启动
  4. FCKeditor编辑器
  5. 完整版—Android studio入门教程
  6. 【雷达通信】合成孔径雷达(SAR)的点目标仿真matlab源码
  7. 如何把电脑的文件同步到云盘
  8. 学计算机的鼓励,鼓励学习的经典语句
  9. java生成二维码图片(有logo),并在图片下方附文字
  10. Javascript高级程序设计--读书笔记--第八章BOM