IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

jRaiser揭秘――事件监听兼容处理

163 UED Team 2010-05-17 13:08:38 累计浏览 2,730 次
本机暂存

在事件监听处理方面,IE提供了attachEvent和detachEvent两个接口,而Firefox提供的是addEventListener和removeEventListener。最简单的兼容性处理就是封装这两套接口:
function addEvent(elem, eventName, handler) {
  if (elem.attachEvent) {
    elem.attachEvent(”on” + eventName, handler);
  } else if (elem.addEventListener) {
    elem.addEventListener(eventName, handler, false);
  }
}

function removeEvent(elem, eventName, handler) {
  if (elem.detachEvent) {
    elem.detachEvent(”on” + eventName, handler);
  } else if (elem.removeEventListener) {
    elem.removeEventListener(eventName, handler, false);
  }
}

然而,上面两个函数还没把问题完全解决。Firefox下,事件处理函数中的this指向被监听元素本身,而在IE下则不然。要解决这个问题,我首先想到的是prototype框架中的function.prototype.bind(下面简称bind):
function addEvent(elem, eventName, handler) {
  if (elem.attachEvent) {
    elem.attachEvent(”on” + eventName, handler.bind(elem));
  } else if (elem.addEventListener) {
    elem.addEventListener(eventName, handler, false);
  }
}

这样做的结果是,再也无法移除事件处理函数。原因在于,移除事件处理函数时需要传入该函数的引用,而bind方法返回的是一个新函数而不是handler本身,且这个新函数的引用并没有保存下来。既然如此,只要把bind返回的引用给保存下来就好了。而存储结构就是个关键点。

首先,一个页面中有任意个元素,所以每个元素都要有一个集合,每个元素所有事件处理函数的引用就存放在这个集合中。
element.events = {};

其次,一个元素有多种事件(click、mouseover、mouseout等),同一个函数可以添加为多种事件的处理函数,所以每种事件的存储也必须是独立的。
elements.events.click = {};
element.events.mouseover = {};

最后,为每个事件处理函数添加一个唯一标识,以便在集合找到它。最简单的标识方法是数据库中常用的自动编号(递增)。

结合以上三点,编写一个生成事件代理的函数:
var eventId = 0;
function delegate(elem, eventName, handler) {
  var events = elem.events = elem.events || {}, // 创建事件集合
    id = handler.eventId = handler.eventId || ++eventId; // 生成事件标识
  events[eventName] = events[eventName] || {}; // 创建某种事件的集合

  var trueHandler = function(e) { // 真正被添加的事件处理函数
    handler.call(elem, e);
  };

  events[eventName][id] = trueHandler; // 记下函数的引用
}

相对应地,还要写一个获取事件代理的函数:
function getDelegate(elem, eventName, handler) {
  try { return elem.events[eventName][handler.eventId]; } catch (e) {}
}

这里用try…catch的原因是,避免elem.events和elem.events[eventName]未创建时出错。

最后addEvent和removeEvent改进如下:
function addEvent(elem, eventName, handler) {
  handler = delegate(elem, eventName, handler);
  if (elem.attachEvent) {
    elem.attachEvent(”on” + eventName, handler);
  } else if (elem.addEventListener) {
    elem.addEventListener(eventName, handler, false);
  }
}

function removeEvent(elem, eventName, handler) {
  handler = getDelegate(elem, eventName, handler);
  if (elem.detachEvent) {
    elem.detachEvent(”on” + eventName, handler);
  } else if (elem.removeEventListener) {
    elem.removeEventListener(eventName, handler, false);
  }
}

其实delegate函数中还可以做更多的事情,有兴趣者请自行研究jRaiser.event对象。

同分类推荐文章

  1. translateZ() (2026-06-25 21:18:56)
  2. translateY() (2026-06-25 21:17:56)
  3. translateX() (2026-06-25 21:16:01)

查看更多 前端 文章 →

建议继续学习

  1. JQuery实现Excel表格呈现 (累计阅读 48,350)
  2. 深入理解Javascript之执行上下文(Execution Context) (累计阅读 18,406)
  3. 从输入 URL 到页面加载完成的过程中都发生了什么事情? (累计阅读 15,934)
  4. 图片动态局部毛玻璃模糊效果的实现 (累计阅读 14,849)
  5. 天朝第二代身份证号码的验证机制 (累计阅读 14,764)
  6. HTML 5 的data-* 自定义属性 (累计阅读 14,349)
  7. 分享一个JQUERY颜色选择插件 (累计阅读 14,224)
  8. 什么是全栈工程师? (累计阅读 14,039)
  9. 快速排序(Quicksort)的Javascript实现 (累计阅读 11,735)
  10. 7 天打造前端性能监控系统 (累计阅读 11,190)